Data visualization is a critical tool in the data analysis process. Visualization tasks can range from generating fundamental distribution plots to understanding the interplay of complex influential variables in machine learning algorithms. In this chapter we focus on the use of visualization for initial data exploration.
Visual data exploration is a mandatory intial step whether or not more formal analysis follows. When combined with descriptive statistics (Chapter~), visualization provides an effective way to identify summaries, structure, relationships, differences, and abnormalities in the data. Often times no elaborate analysis is necessary as all the important conclusions required for a decision are evident from simple visual examination of the data (REF: Box and Hunter). Other times, data exploration will be used to help guide the data cleaning, feature selection, and sampling process.
Regardless, visual data exploration is about investigating the characteristics of your data set. To do this, we typically create numerous plots in an interactive fashion. This chapter will show you how to create plots that answer some of the fundamental questions we typically have of our data.
library(tidyverse)
library(caret)
(ames <- AmesHousing::make_ames())
Visualizing Continuous Variables
A variable is continuous if it can take any of an infinite set of ordered values. There are several different plots that can effectively communicate the different features of continuous variables. Features we are generally interested in include:
- Measures of location
- Measures of spread
- Asymmetry
- Outliers
- Gaps
Histograms are often overlooked, yet they are a very efficient means for communicating these features of continuous variables. Formulated by Karl Pearson, histograms display numeric values on the x-axis where the continuous variable is broken into intervals (aka bins) and the the y-axis represents the frequency of observations that fall into that bin. Histograms quickly signal what the most common observations are for the variable being assessed (the higher the bar the more frequent those values are observed in the data); they also signal the shape (spread and symmetr) of your data by illustrating if the observed values cluster towards one end or the other of the distribution.
To get a quick sense of how sales prices are distributed across the 2,930 properties in the ames data we can generate a simple histogram by applying ggplot’s geom_histogram function. This histogram tells us several important features about our variable:
- Measures of location: We can see the most common
Sale_Price is around the low $100K.
- Measures of spread: Our
Sale_Price ranges from near zero to over $700K.
- Asymmetry:
Sale_Price is skewed right (a common issue with financial data). Depending on the analytic technique we may want to apply later on this suggests we will likely need to transform this variable.
- Outliers: It appears that there are some large values far from the other
Sale_Price values. Whether these are outliers in the mathematical sense or outliers to be concerned about is another issue but for now we at least know they exist.
- Gaps: We see a gap exists between
Sale_Price values around $650K and $700K+.
ggplot(ames, aes(Sale_Price)) +
geom_histogram()

By default, geom_histogram() will divide your data into 30 equal bins or intervals. Since sales prices range from $12,789 - $755,000, dividing this range into 30 equal bins means the bin width is $24,740. So the first bar will represent the frequency of Sale_Price values that range from about $12,500 to about $37,500, the second bar represents the income range from about 37,500 to 62,300, and so on.
However, we can control this parameter by changing the bin width argument in geom_histogram. By changing the bin width when doing exploratory analysis you can get a more detailed picture of the relative densities of the distribution. For instance, in the default histogram there was a bin of $136,000 - $161,000 values that had the highest frequency but as the histograms that follow show, we can gather more information as we adjust the binning.
p1 <- ggplot(ames, aes(Sale_Price)) +
geom_histogram(binwidth = 100000) +
ggtitle("Bin width = $100,000")
p2 <- ggplot(ames, aes(Sale_Price)) +
geom_histogram(binwidth = 50000) +
ggtitle("Bin width = $50,000")
p3 <- ggplot(ames, aes(Sale_Price)) +
geom_histogram(binwidth = 5000) +
ggtitle("Bin width = $5,000")
p4 <- ggplot(ames, aes(Sale_Price)) +
geom_histogram(binwidth = 1000) +
ggtitle("Bin width = $1,000")
gridExtra::grid.arrange(p1, p2, p3, p4, ncol = 2)

Overall, the histograms consistently show the most common income level to be right around $130,000. We can also find the most frequent bin by combining ggplot2::cut_width (ggplot2::cut_interval and ggplot2::cut_number are additional options) with dplyr::count. We see that the most frequent bin when using increments of $5,000 is $128,000 - $132,000.
ames %>%
count(cut_width(Sale_Price, width = 5000)) %>%
arrange(desc(n))
Our histogram with binwidth = 1000 also shows us that there are spikes at specific intervals. This is likely due to home sale prices usually occuring around increments of $5,000. In addition to our primary central tendency (bins with most frequency), we also get a clearer picture of the spread of our variable and its skewness. This suggests there may be a concern with our variable meeting assumptions of normality. If we were to apply an analytic technique that is sensitive to normality assumptions we would likely need to transform our variable.
We can assess the applicability of a log transformation by adding scale_x_log() to our ggplot visual. This log transformed histogram provides a few new insights:
- There is a slight multimodal effect at the top of the distribution suggesting that houses selling in the $150-170K range are not as common as those selling just below and above that price range.
- It appears the log transformation helps our variable meet normality assumptions. More on this in a second.
- It appears there is a new potential outlier that we did not see earlier. There is at least one observation where the
Sale_Price is near zero. In fact, further investigation identifies two observations, one with a Sale_Price of $12,789 and another at $13,100.
ggplot(ames, aes(Sale_Price)) +
geom_histogram(bins = 100) +
geom_vline(xintercept = c(150000, 170000), color = "red", lty = "dashed") +
scale_x_log10(
labels = scales::dollar,
breaks = c(50000, 125000, 300000)
)

Let’s take a closer look at the second two insights. First, we’ll consider the issue of normality.
If you really want to look at normality, then Q-Q plots are a great visual to assess (Fig xx). This graph plots the cumulative values we have in our data against the cumulative probability of a particular distribution (the default is a normal distribution). In essence, this plot compares the actual value against the expected value that the score should have in a normal distribution. If the data are normally distributed the plot will display a straight (or nearly straight) line. If the data deviates from normality then the line will display strong curvature or “snaking.” These plots illustrate how much the untransformed variable deviates from normality whereas the log transformed values align much closer to a normal distribution.
par(mfrow = c(1, 2))
# non-log transformed
qqnorm(ames$Sale_Price, main = "Untransformed\nNormal Q-Q Plot")
qqline(ames$Sale_Price)
# log transformed
qqnorm(log(ames$Sale_Price), main = "Log Transformed\nNormal Q-Q Plot")
qqline(log(ames$Sale_Price))

I also mentioned how we obtained a new insight regarding a new potential outlier that we did not see earlier. So far our histogram identified potential outliers at the lower end and upper end of the sale price spectrum. Unfortunately histograms are not very good at delineating outliers. Rather, we can use a boxplot which does a better job identifying specific outliers.
Boxplots are an alternative way to illustrate the distribution of a variable and is a concise way to illustrate the standard quantiles and outliers of data. As Figure XX indicates, the box itself extends, left to right, from the 1st quartile to the 3rd quartile. This means that it contains the middle half of the data. The line inside the box is positioned at the median. The lines (whiskers) coming out either side of the box extend to 1.5 interquartile ranges (IQRs) from the quartiles. These generally include most of the data outside the box. More distant values, called outliers, are denoted separately by individual points. Now we have a more analytically specific approach to identifying outliers.
There are two efficient graphs to get an indication of potential outliers in our data. The classic boxplot on the left will identify points beyond the whiskers which are beyond \(1.5*IQR\) from the first and third quantile. This illustrates there are several additional observations that we may need to assess as outliers that were not evident in our histogram. However, when looking at a boxplot we lose insight into the shape of the distribution. A violin plot on the right provides us a similar chart as the boxplot but we lose insight into the quantiles of our data and outliers are not plotted (hence the reason I plot geom_point prior to geom_violin). Violin plots will come in handy later when we start to visualize multiple distributions along side each other.
p1 <- ggplot(ames, aes("var", Sale_Price)) +
geom_boxplot(outlier.alpha = .25) +
scale_y_log10(
labels = scales::dollar,
breaks = quantile(ames$Sale_Price)
)
p2 <- ggplot(ames, aes("var", Sale_Price)) +
geom_point() +
geom_violin() +
scale_y_log10(
labels = scales::dollar,
breaks = quantile(ames$Sale_Price)
)
gridExtra::grid.arrange(p1, p2, ncol = 2)

The boxplot starts to answer the question of what potential outliers exist in your data. Outliers in data can distort predictions and affect their accuracy. Consequently, its important to understand if outliers are present and, if so, which observations are considered outliers. Boxplots provide a visual assessment of potential outliers while the outliers package provides a number of useful functions to systematically extract these outliers. The most useful function is the scores function, which computes normal, t, chi-squared, IQR and MAD scores of the given data which you can use to find observation(s) that lie beyond a given value.
Here, I use the outliers::score function to extract those observations beyond the whiskers in our boxplot and then use a stem and leaf plot to assess them. A stem and leaf plot is a special table where each data value is split into a “stem” (the first digit or digits) and a “leaf” (usually the second digit). Since the decimal point is located 5 digits to the right of the “|”" the last stem of “7” and and first leaf of “5” means an outlier exists at around $750,000. The last stem of “7” and and second leaf of “6” means an outlier exists at around $760,000. This is a concise way to see approximately where our outliers are. In fact, I can now see that I have 28 lower end outliers ranging from $10,000-$60,000 and 32 upper end outliers ranging from $450,000-$760,000.
outliers <- outliers::scores(log(ames$Sale_Price), type = "iqr", lim = 1.5)
stem(ames$Sale_Price[outliers])
The decimal point is 5 digit(s) to the right of the |
0 | 1134444445555555666666666666
1 |
2 |
3 |
4 | 56666777788899
5 | 000445566889
6 | 1123
7 | 56
Another useful plot for univariate assessment includes the smoothed histogram in which a non-parametric approach is used to estimate the density function. Displaying in density form just means the y-axis is now in a probability scale where the proportion of the given value (or bin of values) to the overall population is displayed. In essence, the y-axis tells you the estimated probability of the x-axis value occurring. This results in a smoothed curve known as the density plot that allows us visualize the distribution. Since the focus of a density plot is to view the overall distribution rather than individual bin observations we lose insight into how many observations occur at certain x values. Consequently, it can be helpful to use geom_rug with geom_density to highlight where clusters, outliers, and gaps of observations are occuring.
p1 <- ggplot(ames, aes(Sale_Price)) +
geom_density()
p2 <- ggplot(ames, aes(Sale_Price)) +
geom_density() +
geom_rug()
gridExtra::grid.arrange(p1, p2, nrow = 1)

Often you will see density plots layered onto histograms. To layer the density plot onto the histogram we need to first draw the histogram but tell ggplot to have the y-axis in density form rather than count. You can then add the geom_density function to add the density plot on top.
ggplot(ames, aes(Sale_Price)) +
geom_histogram(aes(y = ..density..),
binwidth = 5000, color = "grey30", fill = "white") +
geom_density(alpha = .2, fill = "antiquewhite3")

You may also be interested to see if there are any systematic groupings with how the data is structured. For example, using base R’s plot function with just the Sale_Price will plot the sale price versus the index (row) number of each observation. In the plot below we see a pattern which indicates that groupings of homes with high versus lower sale prices are concentrated together throughout the data set.
plot(ames$Sale_Price)

There are also a couple plots that can come in handy when dealing with smaller data sets. For example, the dotplot below provides more clarity than the histogram for viewing the distribution of mpg in the built-in mtcars dataset with only 32 observations. An alternative to this would be using a strip chart (see stripchart).
p1 <- ggplot(mtcars, aes(x = mpg)) +
geom_dotplot(method = "histodot", binwidth = 1) +
ggtitle("dotplot")
p2 <- ggplot(mtcars, aes(x = mpg)) +
geom_histogram(binwidth = 1) +
ggtitle("histogram")
gridExtra::grid.arrange(p1, p2, nrow = 1)

As demonstrated, several plots exist for examining univariate continuous variables. Several exampes were provided here but still more exist (i.e. frequency polygon, beanplot, shifted histograms). There is some general advice to follow such as histograms being poor for small data sets, dotplots being poor for large data sets, histograms being poor for identifying outlier cut-offs, boxplots being good for outliers but obscuring multimodality. Conseqently, it is important to draw a variety of plots. Moreover, it is important to adjust parameters within plots (i.e. binwidth, axis transformation for skewed data) to get a comprehensive picture of the variable of concern.
Next, we’ll assess how we can gain insight into distributions of categorical variables.
Visualizing Categorical Variables
A categorical variable is a variable that can take on one of a limited, and usually fixed, number of possible values, assigning each individual or other unit of observation to a particular group or nominal category on the basis of some qualitative property (i.e. gender, grade, manufacturer). There are a few different plots that can effectively communicate features of categorical variables. Features we are generally interested in include:
- Count of each category
- Proportion of each category
- Imbalanced categories
- Mislabeled categories
Bar charts are one of the most commonly used data visualizations for categorical variables. Bar charts display the levels of a categorical variable of interest (typically) along the x-axis and the length of the bar illustrates the value along the y-axis. Consequently, the length of the bar is the primary visual cue in a bar chart and in a univariate visualization this length represents counts of cases in that particular level.
If we look at the general zoning classification for each property sold in our ames dataset we see that the majority of all properties fall within one category. Here, geom_bar simply counts up all observations for each zoning level.
ggplot(ames, aes(MS_Zoning)) +
geom_bar()

Here, MS_Zoning represents a nominal categorical variable where there is no logical ordering of the labels; they simply represent mutually exclusive levels within our variable. To get better clarity of nominal variables we can make some refinements. Here I use dplyr::count to count the observations in each level prior to plotting. In the second plot I use mutate to compute the percent that each level makes up of all observations. I then feed these summarized data into ggplot where I can reorder the MS_Zoning variable from most frequent to least and then apply coord_flip to rotate the plot and make it easier to read the level categories. Also, notice that now I feeding an x (MS_Zoning) and y (n in the left plot and pct in the right plot) arguments so I apply geom_col rather than geom_bar.
# total count
p1 <- ames %>%
count(MS_Zoning) %>%
ggplot(aes(reorder(MS_Zoning, n), n)) +
geom_col() +
coord_flip() +
ggtitle("Total count")
# percent of whole
p2 <- ames %>%
count(MS_Zoning) %>%
mutate(pct = n / sum(n)) %>%
ggplot(aes(reorder(MS_Zoning, pct), pct)) +
geom_col() +
coord_flip() +
ggtitle("Percent of whole")
gridExtra::grid.arrange(p1, p2, nrow = 1)

Now we can see that properties zoned as residential low density make up nearly 80% of all observations . We also see that properties zoned as aggricultural (A_agr), industrial (I_all), commercial (C_all), and residential high density make up a very small amount of observations. In fact, below we see that these imbalanced category levels each make up less than 1% of all observations.
ames %>%
count(MS_Zoning) %>%
mutate(pct = n / sum(n)) %>%
arrange(pct)
This imbalanced nature can cause problems in future analytic models so it may make sense to combine these infrequent levels into an “other” category. An easy way to do that is to use fct_lump. Here we use n = 2 to retain the top 2 levels in our variable and condense the remaining into an “other” category. You can see that this combined category still represents less than 10% of all observations.
ames %>%
mutate(MS_Zoning = fct_lump(MS_Zoning, n = 2)) %>%
count(MS_Zoning) %>%
mutate(pct = n / sum(n)) %>%
ggplot(aes(reorder(MS_Zoning, pct), pct)) +
geom_col() +
coord_flip()

Basic bar charts such as these are great when the number of category levels is smaller. However, as the number of levels increase the thick nature of the bar can be distracting. Cleveland dot plots and lollipop charts are useful for assessing the frequency or proportion of many levels while minizing the amount of ink on the graphic.
For example, if we assess the frequencies and proportions of home sales by the 38 different neighborhoods a dotplot simplifies the chart.
ames %>%
count(Neighborhood) %>%
mutate(pct = n / sum(n)) %>%
ggplot(aes(pct, reorder(Neighborhood, pct))) +
geom_point()

Similar to the Cleveland dot plot, a lollipop chart minimizes the visual ink but uses a line to draw the readers attention to the specific x-axis value achieved by each category. In the lollipop chart we use geom_segment to plot the lines and we explicitly state that we want the lines to start at x = 0 and extend to the neighborhood value with xend = pct. We simply need to include y = neighborhood and yend = neighborhood to tell R the lines are horizontally attached to each neighborhood.
ames %>%
count(Neighborhood) %>%
mutate(pct = n / sum(n)) %>%
ggplot(aes(pct, reorder(Neighborhood, pct))) +
geom_point() +
geom_segment(aes(x = 0, xend = pct, y = Neighborhood, yend = Neighborhood), size = .15)

Sometimes we have categorical data that have natural, ordered categories. These types of categorical variables can be ordinal or interval. An ordinal variable is one in which the order of the values can be important but the differences between each one is not really known. For example, our ames data categorizes the quality of kitchens into five buckets and these buckets have a natural order that is not captured with a regular bar chart.
ggplot(ames, aes(Kitchen_Qual)) +
geom_bar()

Here, rather than order by frequency it may be important to order the bars by the natural order of the quality lables: Poor, Fair, Typical, Good, Excellent. This can provide better insight into where most observations fall within this spectrum of quality. To do this we reorder the factor levels with fct_relevel and now its easier to see that most homes have average to slightly above average quality kitchens.
ames %>%
mutate(Kitchen_Qual = fct_relevel(Kitchen_Qual, "Poor", "Fair", "Typical", "Good")) %>%
ggplot(aes(Kitchen_Qual)) +
geom_bar()

We may also have a categorical variable that has set intervals and may even be identified by integer values. For example, our data identifies the month each home was sold but uses integer values to represent the months. In this case we do not need to reorder our factor levels but we should ensure we visualize these as discrete factor levels (note how I apply factor(Mo_Sold) within ggplot) so that the home sale counts are appropriately bucketed into each month.
p1 <- ggplot(ames, aes(Mo_Sold)) +
geom_bar()
p2 <- ggplot(ames, aes(factor(Mo_Sold))) +
geom_bar()
gridExtra::grid.arrange(p1, p2, nrow = 2)

Bar charts can also illustrate how our missing values are disbursed across categorical variables. Using the MASS::survey data (since our ames data does not have any missing data) we can make small multiples (more on this in the next section) using facet_wrap to visualize the NAs.
MASS::survey %>%
select(Sex, Exer, Smoke, Fold, Clap, M.I) %>%
gather(var, value, Sex:M.I) %>%
ggplot(aes(value)) +
geom_bar() +
facet_wrap(~ var, scales = "free")
attributes are not identical across measure variables;
they will be dropped

Or in some cases observations are not labeled correctly. If we look at the Embarked variable in the titanic package we see that the levels are labeled as C, Q, and S; however, there are two cases that have no label (these values are coded as "" in the actual data set). These are missing values that are just not coded as NAs. For modeling purposes we would likely recode these as either NAs or impute them as one of the other three levels (C, Q, or S).
ggplot(titanic::titanic_train, aes(Embarked)) +
geom_bar()

Bar charts and their cousins are a simple form of visual display, yet they can provide much information about our categorical variables. Whether viewing nominal, ordinal, or interval data we can make minor adjustments in our bar charts to highlight the important features of our variables.
Visualizing Relationships and Associations for a Continuous Response
Having a solid understanding of univariate distributions is important; however, most analyses want to take the next step understand associations and relationships across different variables. Features we are generally interested in include:
- Associations
- Outliers
- Clusters
- Gaps
- Barriers
- Change points
One of the most popular plots to assess association is the scatter plot. The scatter plot helps us to see multiple features between two continuous variables. Here we look the relationship between Sale_Price and total above ground square footage (Gr_Liv_Area). A few features that pop out from this plot includes:
- Associations: There is a positive relationship between these two variables. As total above ground square footage increases the sale price also increases.
- Outliers: Several outliers appear in multiple directions. Two outliers appear at the top of the chart suggesting these are larger than normal homes that sold for very high prices. We also see three outliers at the far right of the chart suggesting these homes have very large square footage but sold for average sale prices.
- Clusters: Give the large number of points there is a lot of overplotting, which is why I incorporated
alpha = .3 to increase transparency. This allows us to see the clustering of data points in the center of the variable relationship.
- Barriers: The outer limits of our point clustering shows us that there are limitations on the sale price for given ranges of square footage. For example, homes with less than 1,000 square feet above ground appear to have a price ceiling of $200,000 or less.
ggplot(ames, aes(x = Gr_Liv_Area, y = Sale_Price)) +
geom_point(alpha = .3)

This relationship appears to be fairly linear but it is unclear. We can add trend lines to assess the linearity. In the below plot we add a linear line with geom_smooth(method = "lm") and then we add a non-linear line (the second geom_smooth with a specified method adds uses a generalized additive model). This allows us to assess how non-linear a relationship may be. Our new plot shows that for homes with less than 2,250 square feet the relationship is fairly linear; however, beyond 2,250 square feet we see strong deviations from linearity.
Also, note the funneling in the left scatter plot. This is called heteroskedasticity (non-constant variance) and this can cause concerns with certain future modeling approaches (i.e. forms of linear regression). We can assess if transforming our variables can alleviate this concern by adding scale_?_log10. The right plot shows that transforming our variables makes our variability across the plot more constant. We see that for the majority of the plot the relationship is now linear with the exception of the two ends where we see the non-linear line being pulled down. This suggests that there are some influential observations with low and high square footage that are pulling the expected sale price down.
p1 <- ggplot(ames, aes(x = Gr_Liv_Area, y = Sale_Price)) +
geom_point(alpha = .3) +
geom_smooth(method = "lm", se = FALSE, color = "red", lty = "dashed") +
geom_smooth(se = FALSE, lty = "dashed") +
ggtitle("Non-transformed variables")
p2 <- ggplot(ames, aes(x = Gr_Liv_Area, y = Sale_Price)) +
geom_point(alpha = .3) +
geom_smooth(method = "lm", se = FALSE, color = "red", lty = "dashed") +
geom_smooth(se = FALSE, lty = "dashed") +
scale_x_log10() +
scale_y_log10() +
ggtitle("log-transformed variables")
gridExtra::grid.arrange(p1, p2, nrow = 1)

Scatter plots can also signal distinct clustering or gaps. For example, if we plot Sale_Price versus Garage_Area (left) we see a couple areas of concentrated points. By incorporating a density plot (middle) we can draw attention to the centers of these clusters which appears to be located at homes with zero garage square footage and homes with just over 250 square feet and just under 500 square feet of garage area. We can also change our plot to a hexbin plot, that replaces bunches of points with a larger hexagonal symbol. This provides us with a heatmap-like plot to signal highly concentrated regions. It also does a better job identifying gaps in our data where no observations exist.
p1 <- ggplot(ames, aes(x = Garage_Area, y = Sale_Price)) +
geom_point(alpha = .2)
p2 <- ggplot(ames, aes(x = Garage_Area, y = Sale_Price)) +
geom_point(alpha = .2) +
geom_density2d()
p3 <- ggplot(ames, aes(x = Garage_Area, y = Sale_Price)) +
geom_hex(bins = 50, show.legend = FALSE)
gridExtra::grid.arrange(p1, p2, p3, nrow = 1)

When assessing a continuous variable against a categorical variable a stip plot will form. Here we assess the Sale_Price to the number of above ground bedrooms (Bedroom_AbvGr). Due to the size of this data set, the top left strip plot has a lot of overlaid data points. We can use geom_jitter to add a little variation to our plot (top right), which allows us to see where heavier concentrations of points exist. Alternatively, we can use boxplots and violin plots to compare the distributions of Sale_Price to Bedroom_AbvGr. Each plot provides different insights to the different features (i.e. outliers, clustering, median values).
p1 <- ggplot(ames, aes(x = factor(Bedroom_AbvGr), y = Sale_Price)) +
geom_point(alpha = .3)
p2 <- ggplot(ames, aes(x = factor(Bedroom_AbvGr), y = Sale_Price)) +
geom_jitter(alpha = .5, width = .2)
p3 <- ggplot(ames, aes(x = factor(Bedroom_AbvGr), y = Sale_Price)) +
geom_boxplot()
p4 <- ggplot(ames, aes(x = factor(Bedroom_AbvGr), y = Sale_Price)) +
geom_violin()
gridExtra::grid.arrange(p1, p2, p3, p4, nrow = 2)

An alternative approach to view the distribution of a continuous variable across multiple categories includes overlaying distribution plots. For example, we could assess the Sale_Price of homes across the overall quality of homes. We can do this with a frequency polygon (left), which display the outline of a histogram. However, since some quality levels have very low counts it is tough to see the distribution of costs within each category. A better approach is to overlay density plots which allows us to see how each quality level’s distribution differs from one another.
p1 <- ggplot(ames, aes(x = Sale_Price, color = Overall_Qual)) +
geom_freqpoly() +
scale_x_log10(breaks = c(50, 150, 400, 750) * 1000, labels = scales::dollar)
p2 <- ggplot(ames, aes(x = Sale_Price, color = Overall_Qual, fill = Overall_Qual)) +
geom_density(alpha = .15) +
scale_x_log10(breaks = c(50, 150, 400, 750) * 1000, labels = scales::dollar)
gridExtra::grid.arrange(p1, p2, nrow = 2)

When there are many levels in a categorical variable, overlaid plots become difficult to decipher. Rather than overlay plots, we can also use small multiples to compare the distribution of a continuous variable. Joyplots provide a form of small multiples by partially overlapping distribution plots. They can be quite useful for visualizing changes in continuous distributions over discrete variable levels. In this example I use the ggjoy package which provides an add-on geom_joy for ggplot. Now we get a much clearer picture how the sales price differs for each quality level.
ggplot(ames, aes(x = Sale_Price, y = Overall_Qual)) +
ggjoy::geom_joy() +
scale_x_continuous(labels = scales::dollar)

In most analyses, data are usually multivariate by nature, and the analytics are designed to capture and measure multivariate relationships. Visual exploration should there fore also incorporate this important aspect. We can extend these basic principles and add in additional features to assess multidimensional relationships. One approach is to add additional variables with features such as color, shape, or size. For example, here we compare the sales price to above ground square footage of homes with and without central air conditioning. We can see that there are far more homes with central air and that those homes without central air tend to have less square footage and sell for lower sales prices.
ggplot(ames, aes(x = Gr_Liv_Area, y = Sale_Price, color = Central_Air, shape = Central_Air)) +
geom_point(alpha = .3) +
scale_x_log10() +
scale_y_log10()

However, as before, when there are many levels in a categorical variable it becomes hard to compare differences by only incorporating color or shape features. An alternative is to create small multiples. Here we compare the relationship between sales price and above ground square footage; however, we assess how this relationship may differ across the different house styles (i.e. one story, two story, etc.).
ggplot(ames, aes(x = Gr_Liv_Area, y = Sale_Price)) +
geom_point(alpha = .3) +
scale_x_log10() +
scale_y_log10(labels = scales::dollar) +
facet_wrap(~ House_Style, nrow = 2) +
theme_bw()

We can start to add several of the features discussed in this section to highlight mutlivariate features. For example, here we assess the relationship between sales price and above ground square footable for homes with and without central air conditioning and across the different housing styles. For each house style and central air category we can see where the values are clustered and how the linear relationship changes. For all home styles, houses with central air have a higher selling price with a steeper slope than those without central air. Also, those plots without density markings and linear lines for the no central air category (red) tell us that there are no more than one observation in these groups; so this identifies gaps across multivariate categories of interest.
ggplot(ames, aes(x = Gr_Liv_Area, y = Sale_Price, color = Central_Air, shape = Central_Air)) +
geom_point(alpha = .3) +
geom_density2d(alpha = .5) +
geom_smooth(method = "lm", se = FALSE) +
scale_x_log10() +
scale_y_log10(labels = scales::dollar) +
facet_wrap(~ House_Style, nrow = 2) +
ggtitle("Sale Price vs. Above Ground Sq.Ft",
subtitle = "How does central air and house style influence this relationship?") +
theme_bw()

Parallel coordinate plots (PCP) are also a great way to visualize continuous variables across multiple variables. In these plots, a vertical axis is drawn for each variable. Then each observation is represented by drawing a line that connects its values on the different axes, thereby creating a multivariate profile. To create a PCP, we can use ggparcoord from the GGally package. By default, ggparcoord will standardize the variables based on a Z-score distribution; however, in this example I center scale the variables. One benefit of of a PCP is that you can visualize your observations across continuous and categorical variables. In this example I include Overall_Qual which is an ordered factor with levels “Very Poor”, “Poor”, “Fair”, …, “Excellent”, “Very Excellent” having values of 1-10.
variables <- c("Sale_Price", "Year_Built", "Year_Remod_Add", "Overall_Qual")
ames %>%
select(variables) %>%
ggparcoord(alpha = .05, scale = "center")

Visualizing Relationships and Associations for a Categorical Response
- facetted bar charts
- mosaic plot
- heatmap
LS0tCnRpdGxlOiAiQUJBUjogQ2hhcHRlciAzIgpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sKLS0tCgpEYXRhIHZpc3VhbGl6YXRpb24gaXMgYSBjcml0aWNhbCB0b29sIGluIHRoZSBkYXRhIGFuYWx5c2lzIHByb2Nlc3MuICBWaXN1YWxpemF0aW9uIHRhc2tzIGNhbiByYW5nZSBmcm9tIGdlbmVyYXRpbmcgZnVuZGFtZW50YWwgZGlzdHJpYnV0aW9uIHBsb3RzIHRvIHVuZGVyc3RhbmRpbmcgdGhlIGludGVycGxheSBvZiBjb21wbGV4IGluZmx1ZW50aWFsIHZhcmlhYmxlcyBpbiBtYWNoaW5lIGxlYXJuaW5nIGFsZ29yaXRobXMuICBJbiB0aGlzIGNoYXB0ZXIgd2UgZm9jdXMgb24gdGhlIHVzZSBvZiB2aXN1YWxpemF0aW9uIGZvciBpbml0aWFsICpkYXRhIGV4cGxvcmF0aW9uKi4gCgpWaXN1YWwgZGF0YSBleHBsb3JhdGlvbiBpcyBhIG1hbmRhdG9yeSBpbnRpYWwgc3RlcCB3aGV0aGVyIG9yIG5vdCBtb3JlIGZvcm1hbCBhbmFseXNpcyBmb2xsb3dzLiAgV2hlbiBjb21iaW5lZCB3aXRoIGRlc2NyaXB0aXZlIHN0YXRpc3RpY3MgKENoYXB0ZXJ+XHJlZntjaDI6ZGVzY3JpcHRpdmV9KSwgdmlzdWFsaXphdGlvbiBwcm92aWRlcyBhbiBlZmZlY3RpdmUgd2F5IHRvIGlkZW50aWZ5IHN1bW1hcmllcywgc3RydWN0dXJlLCByZWxhdGlvbnNoaXBzLCBkaWZmZXJlbmNlcywgYW5kIGFibm9ybWFsaXRpZXMgaW4gdGhlIGRhdGEuICBPZnRlbiB0aW1lcyBubyBlbGFib3JhdGUgYW5hbHlzaXMgaXMgbmVjZXNzYXJ5IGFzIGFsbCB0aGUgaW1wb3J0YW50IGNvbmNsdXNpb25zIHJlcXVpcmVkIGZvciBhIGRlY2lzaW9uIGFyZSBldmlkZW50IGZyb20gc2ltcGxlIHZpc3VhbCBleGFtaW5hdGlvbiBvZiB0aGUgZGF0YSAqKihSRUY6IEJveCBhbmQgSHVudGVyKSoqLiAgT3RoZXIgdGltZXMsIGRhdGEgZXhwbG9yYXRpb24gd2lsbCBiZSB1c2VkIHRvIGhlbHAgZ3VpZGUgdGhlIGRhdGEgY2xlYW5pbmcsIGZlYXR1cmUgc2VsZWN0aW9uLCBhbmQgc2FtcGxpbmcgcHJvY2Vzcy4gIAoKUmVnYXJkbGVzcywgdmlzdWFsIGRhdGEgZXhwbG9yYXRpb24gaXMgYWJvdXQgaW52ZXN0aWdhdGluZyB0aGUgY2hhcmFjdGVyaXN0aWNzIG9mIHlvdXIgZGF0YSBzZXQuICBUbyBkbyB0aGlzLCB3ZSB0eXBpY2FsbHkgY3JlYXRlIG51bWVyb3VzIHBsb3RzIGluIGFuIGludGVyYWN0aXZlIGZhc2hpb24uICBUaGlzIGNoYXB0ZXIgd2lsbCBzaG93IHlvdSBob3cgdG8gY3JlYXRlIHBsb3RzIHRoYXQgYW5zd2VyIHNvbWUgb2YgdGhlIGZ1bmRhbWVudGFsIHF1ZXN0aW9ucyB3ZSB0eXBpY2FsbHkgaGF2ZSBvZiBvdXIgZGF0YS4gIAoKCmBgYHtyIHBhY2thZ2VzLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpsaWJyYXJ5KHRpZHl2ZXJzZSkKbGlicmFyeShjYXJldCkKbGlicmFyeShHR2FsbHkpCmBgYAoKYGBge3IgZGF0YSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KKGFtZXMgPC0gQW1lc0hvdXNpbmc6Om1ha2VfYW1lcygpKQpgYGAKCmBgYHtyIHNldHVwLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCBlY2hvPUZBTFNFfQp0aGVtZV9zZXQodGhlbWVfbWluaW1hbCgpKQprbml0cjo6b3B0c19jaHVuayRzZXQod2FybmluZyA9IEZBTFNFLCBtZXNzYWdlID0gRkFMU0UsIGNvbGxhcHNlID0gVFJVRSwgZmlnLmFsaWduID0gJ2NlbnRlcicpCmBgYAoKIyBWaXN1YWxpemluZyBDb250aW51b3VzIFZhcmlhYmxlcwoKQSB2YXJpYWJsZSBpcyBjb250aW51b3VzIGlmIGl0IGNhbiB0YWtlIGFueSBvZiBhbiBpbmZpbml0ZSBzZXQgb2Ygb3JkZXJlZCB2YWx1ZXMuIFRoZXJlIGFyZSBzZXZlcmFsIGRpZmZlcmVudCBwbG90cyB0aGF0IGNhbiBlZmZlY3RpdmVseSBjb21tdW5pY2F0ZSB0aGUgZGlmZmVyZW50IGZlYXR1cmVzIG9mIGNvbnRpbnVvdXMgdmFyaWFibGVzLiAgRmVhdHVyZXMgd2UgYXJlIGdlbmVyYWxseSBpbnRlcmVzdGVkIGluIGluY2x1ZGU6CgotIE1lYXN1cmVzIG9mIGxvY2F0aW9uCi0gTWVhc3VyZXMgb2Ygc3ByZWFkCi0gQXN5bW1ldHJ5Ci0gT3V0bGllcnMKLSBHYXBzCgoKSGlzdG9ncmFtcyBhcmUgb2Z0ZW4gb3Zlcmxvb2tlZCwgeWV0IHRoZXkgYXJlIGEgdmVyeSBlZmZpY2llbnQgbWVhbnMgZm9yIGNvbW11bmljYXRpbmcgdGhlc2UgZmVhdHVyZXMgb2YgY29udGludW91cyB2YXJpYWJsZXMuIEZvcm11bGF0ZWQgYnkgS2FybCBQZWFyc29uLCBoaXN0b2dyYW1zIGRpc3BsYXkgbnVtZXJpYyB2YWx1ZXMgb24gdGhlIHgtYXhpcyB3aGVyZSB0aGUgY29udGludW91cyB2YXJpYWJsZSBpcyBicm9rZW4gaW50byBpbnRlcnZhbHMgKGFrYSBiaW5zKSBhbmQgdGhlIHRoZSB5LWF4aXMgcmVwcmVzZW50cyB0aGUgZnJlcXVlbmN5IG9mIG9ic2VydmF0aW9ucyB0aGF0IGZhbGwgaW50byB0aGF0IGJpbi4gSGlzdG9ncmFtcyBxdWlja2x5IHNpZ25hbCB3aGF0IHRoZSBtb3N0IGNvbW1vbiBvYnNlcnZhdGlvbnMgYXJlIGZvciB0aGUgdmFyaWFibGUgYmVpbmcgYXNzZXNzZWQgKHRoZSBoaWdoZXIgdGhlIGJhciB0aGUgbW9yZSBmcmVxdWVudCB0aG9zZSB2YWx1ZXMgYXJlIG9ic2VydmVkIGluIHRoZSBkYXRhKTsgdGhleSBhbHNvIHNpZ25hbCB0aGUgc2hhcGUgKHNwcmVhZCBhbmQgc3ltbWV0cikgb2YgeW91ciBkYXRhIGJ5IGlsbHVzdHJhdGluZyBpZiB0aGUgb2JzZXJ2ZWQgdmFsdWVzIGNsdXN0ZXIgdG93YXJkcyBvbmUgZW5kIG9yIHRoZSBvdGhlciBvZiB0aGUgZGlzdHJpYnV0aW9uLgoKVG8gZ2V0IGEgcXVpY2sgc2Vuc2Ugb2YgaG93IHNhbGVzIHByaWNlcyBhcmUgZGlzdHJpYnV0ZWQgYWNyb3NzIHRoZSAyLDkzMCBwcm9wZXJ0aWVzIGluIHRoZSBgYW1lc2AgZGF0YSB3ZSBjYW4gZ2VuZXJhdGUgYSBzaW1wbGUgaGlzdG9ncmFtIGJ5IGFwcGx5aW5nIGdncGxvdOKAmXMgYGdlb21faGlzdG9ncmFtYCBmdW5jdGlvblteYmFzZVJoaXN0XS4gVGhpcyBoaXN0b2dyYW0gdGVsbHMgdXMgc2V2ZXJhbCBpbXBvcnRhbnQgZmVhdHVyZXMgYWJvdXQgb3VyIHZhcmlhYmxlOgoKLSBNZWFzdXJlcyBvZiBsb2NhdGlvbjogV2UgY2FuIHNlZSB0aGUgbW9zdCBjb21tb24gYFNhbGVfUHJpY2VgIGlzIGFyb3VuZCB0aGUgbG93ICQxMDBLLgotIE1lYXN1cmVzIG9mIHNwcmVhZDogT3VyIGBTYWxlX1ByaWNlYCByYW5nZXMgZnJvbSBuZWFyIHplcm8gdG8gb3ZlciAkNzAwSy4KLSBBc3ltbWV0cnk6IGBTYWxlX1ByaWNlYCBpcyBza2V3ZWQgcmlnaHQgKGEgY29tbW9uIGlzc3VlIHdpdGggZmluYW5jaWFsIGRhdGEpLiAgRGVwZW5kaW5nIG9uIHRoZSBhbmFseXRpYyB0ZWNobmlxdWUgd2UgbWF5IHdhbnQgdG8gYXBwbHkgbGF0ZXIgb24gdGhpcyBzdWdnZXN0cyB3ZSB3aWxsIGxpa2VseSBuZWVkIHRvIHRyYW5zZm9ybSB0aGlzIHZhcmlhYmxlLgotIE91dGxpZXJzOiBJdCBhcHBlYXJzIHRoYXQgdGhlcmUgYXJlIHNvbWUgbGFyZ2UgdmFsdWVzIGZhciBmcm9tIHRoZSBvdGhlciBgU2FsZV9QcmljZWAgdmFsdWVzLiAgV2hldGhlciB0aGVzZSBhcmUgb3V0bGllcnMgaW4gdGhlIG1hdGhlbWF0aWNhbCBzZW5zZSBvciBvdXRsaWVycyB0byBiZSBjb25jZXJuZWQgYWJvdXQgaXMgYW5vdGhlciBpc3N1ZSBidXQgZm9yIG5vdyB3ZSBhdCBsZWFzdCBrbm93IHRoZXkgZXhpc3QuCi0gR2FwczogV2Ugc2VlIGEgZ2FwIGV4aXN0cyBiZXR3ZWVuIGBTYWxlX1ByaWNlYCB2YWx1ZXMgYXJvdW5kICQ2NTBLIGFuZCAkNzAwSysuICAKCmBgYHtyIGhpc3QxLCBmaWcuaGVpZ2h0PTMsIGZpZy53aWR0aD01fQpnZ3Bsb3QoYW1lcywgYWVzKFNhbGVfUHJpY2UpKSArCiAgZ2VvbV9oaXN0b2dyYW0oKQpgYGAKCkJ5IGRlZmF1bHQsIGBnZW9tX2hpc3RvZ3JhbSgpYCB3aWxsIGRpdmlkZSB5b3VyIGRhdGEgaW50byAzMCBlcXVhbCBiaW5zIG9yIGludGVydmFscy4gU2luY2Ugc2FsZXMgcHJpY2VzIHJhbmdlIGZyb20gXCQxMiw3ODkgLSBcJDc1NSwwMDAsIGRpdmlkaW5nIHRoaXMgcmFuZ2UgaW50byAzMCBlcXVhbCBiaW5zIG1lYW5zIHRoZSBiaW4gd2lkdGggaXMgXCQyNCw3NDAuIFNvIHRoZSBmaXJzdCBiYXIgd2lsbCByZXByZXNlbnQgdGhlIGZyZXF1ZW5jeSBvZiBgU2FsZV9QcmljZWAgdmFsdWVzIHRoYXQgcmFuZ2UgZnJvbSBhYm91dCBcJDEyLDUwMCB0byBhYm91dCBcJDM3LDUwMFteYmluc10sIHRoZSBzZWNvbmQgYmFyIHJlcHJlc2VudHMgdGhlIGluY29tZSByYW5nZSBmcm9tIGFib3V0IDM3LDUwMCB0byA2MiwzMDAsIGFuZCBzbyBvbi4KCkhvd2V2ZXIsIHdlIGNhbiBjb250cm9sIHRoaXMgcGFyYW1ldGVyIGJ5IGNoYW5naW5nIHRoZSBiaW4gd2lkdGggYXJndW1lbnQgaW4gYGdlb21faGlzdG9ncmFtYC4gQnkgY2hhbmdpbmcgdGhlIGJpbiB3aWR0aCB3aGVuIGRvaW5nIGV4cGxvcmF0b3J5IGFuYWx5c2lzIHlvdSBjYW4gZ2V0IGEgbW9yZSBkZXRhaWxlZCBwaWN0dXJlIG9mIHRoZSByZWxhdGl2ZSBkZW5zaXRpZXMgb2YgdGhlIGRpc3RyaWJ1dGlvbi4gRm9yIGluc3RhbmNlLCBpbiB0aGUgZGVmYXVsdCBoaXN0b2dyYW0gdGhlcmUgd2FzIGEgYmluIG9mIFwkMTM2LDAwMCAtIFwkMTYxLDAwMCB2YWx1ZXMgdGhhdCBoYWQgdGhlIGhpZ2hlc3QgZnJlcXVlbmN5IGJ1dCBhcyB0aGUgaGlzdG9ncmFtcyB0aGF0IGZvbGxvdyBzaG93LCB3ZSBjYW4gZ2F0aGVyIG1vcmUgaW5mb3JtYXRpb24gYXMgd2UgYWRqdXN0IHRoZSBiaW5uaW5nLiAKCmBgYHtyIGhpc3QyLCBmaWcuaGVpZ2h0PTUsIGZpZy53aWR0aD04fQoKcDEgPC0gZ2dwbG90KGFtZXMsIGFlcyhTYWxlX1ByaWNlKSkgKwogIGdlb21faGlzdG9ncmFtKGJpbndpZHRoID0gMTAwMDAwKSArCiAgZ2d0aXRsZSgiQmluIHdpZHRoID0gJDEwMCwwMDAiKQoKcDIgPC0gZ2dwbG90KGFtZXMsIGFlcyhTYWxlX1ByaWNlKSkgKwogIGdlb21faGlzdG9ncmFtKGJpbndpZHRoID0gNTAwMDApICsKICBnZ3RpdGxlKCJCaW4gd2lkdGggPSAkNTAsMDAwIikKCnAzIDwtIGdncGxvdChhbWVzLCBhZXMoU2FsZV9QcmljZSkpICsKICBnZW9tX2hpc3RvZ3JhbShiaW53aWR0aCA9IDUwMDApICsKICBnZ3RpdGxlKCJCaW4gd2lkdGggPSAkNSwwMDAiKQoKcDQgPC0gZ2dwbG90KGFtZXMsIGFlcyhTYWxlX1ByaWNlKSkgKwogIGdlb21faGlzdG9ncmFtKGJpbndpZHRoID0gMTAwMCkgKwogIGdndGl0bGUoIkJpbiB3aWR0aCA9ICQxLDAwMCIpCgpncmlkRXh0cmE6OmdyaWQuYXJyYW5nZShwMSwgcDIsIHAzLCBwNCwgbmNvbCA9IDIpCmBgYAoKT3ZlcmFsbCwgdGhlIGhpc3RvZ3JhbXMgY29uc2lzdGVudGx5IHNob3cgdGhlIG1vc3QgY29tbW9uIGluY29tZSBsZXZlbCB0byBiZSByaWdodCBhcm91bmQgXCQxMzAsMDAwLiBXZSBjYW4gYWxzbyBmaW5kIHRoZSBtb3N0IGZyZXF1ZW50IGJpbiBieSBjb21iaW5pbmcgYGdncGxvdDI6OmN1dF93aWR0aGAgKGBnZ3Bsb3QyOjpjdXRfaW50ZXJ2YWxgIGFuZCBgZ2dwbG90Mjo6Y3V0X251bWJlcmAgYXJlIGFkZGl0aW9uYWwgb3B0aW9ucykgd2l0aCBgZHBseXI6OmNvdW50YC4gV2Ugc2VlIHRoYXQgdGhlIG1vc3QgZnJlcXVlbnQgYmluIHdoZW4gdXNpbmcgaW5jcmVtZW50cyBvZiBcJDUsMDAwIGlzIFwkMTI4LDAwMCAtIFwkMTMyLDAwMC4KCmBgYHtyfQphbWVzICU+JQogIGNvdW50KGN1dF93aWR0aChTYWxlX1ByaWNlLCB3aWR0aCA9IDUwMDApKSAlPiUKICBhcnJhbmdlKGRlc2MobikpCmBgYAoKT3VyIGhpc3RvZ3JhbSB3aXRoIGBiaW53aWR0aCA9IDEwMDBgIGFsc28gc2hvd3MgdXMgdGhhdCB0aGVyZSBhcmUgc3Bpa2VzIGF0IHNwZWNpZmljIGludGVydmFscy4gIFRoaXMgaXMgbGlrZWx5IGR1ZSB0byBob21lIHNhbGUgcHJpY2VzIHVzdWFsbHkgb2NjdXJpbmcgYXJvdW5kIGluY3JlbWVudHMgb2YgXCQ1LDAwMC4gIEluIGFkZGl0aW9uIHRvIG91ciBwcmltYXJ5IGNlbnRyYWwgdGVuZGVuY3kgKGJpbnMgd2l0aCBtb3N0IGZyZXF1ZW5jeSksIHdlIGFsc28gZ2V0IGEgY2xlYXJlciBwaWN0dXJlIG9mIHRoZSBzcHJlYWQgb2Ygb3VyIHZhcmlhYmxlIGFuZCBpdHMgc2tld25lc3MuICBUaGlzIHN1Z2dlc3RzIHRoZXJlIG1heSBiZSBhIGNvbmNlcm4gd2l0aCBvdXIgdmFyaWFibGUgbWVldGluZyBhc3N1bXB0aW9ucyBvZiBub3JtYWxpdHkuICBJZiB3ZSB3ZXJlIHRvIGFwcGx5IGFuIGFuYWx5dGljIHRlY2huaXF1ZSB0aGF0IGlzIHNlbnNpdGl2ZSB0byBub3JtYWxpdHkgYXNzdW1wdGlvbnMgd2Ugd291bGQgbGlrZWx5IG5lZWQgdG8gdHJhbnNmb3JtIG91ciB2YXJpYWJsZS4KCldlIGNhbiBhc3Nlc3MgdGhlIGFwcGxpY2FiaWxpdHkgb2YgYSBsb2cgdHJhbnNmb3JtYXRpb24gYnkgYWRkaW5nIGBzY2FsZV94X2xvZygpYCB0byBvdXIgZ2dwbG90IHZpc3VhbFtedHJhbnNmXS4gVGhpcyBsb2cgdHJhbnNmb3JtZWQgaGlzdG9ncmFtIHByb3ZpZGVzIGEgZmV3IG5ldyBpbnNpZ2h0czoKCjEuIFRoZXJlIGlzIGEgc2xpZ2h0IG11bHRpbW9kYWwgZWZmZWN0IGF0IHRoZSB0b3Agb2YgdGhlIGRpc3RyaWJ1dGlvbiBzdWdnZXN0aW5nIHRoYXQgaG91c2VzIHNlbGxpbmcgaW4gdGhlIFwkMTUwLTE3MEsgcmFuZ2UgYXJlIG5vdCBhcyBjb21tb24gYXMgdGhvc2Ugc2VsbGluZyBqdXN0IGJlbG93IGFuZCBhYm92ZSB0aGF0IHByaWNlIHJhbmdlLgoyLiBJdCBhcHBlYXJzIHRoZSBsb2cgdHJhbnNmb3JtYXRpb24gaGVscHMgb3VyIHZhcmlhYmxlIG1lZXQgbm9ybWFsaXR5IGFzc3VtcHRpb25zLiAgTW9yZSBvbiB0aGlzIGluIGEgc2Vjb25kLgozLiBJdCBhcHBlYXJzIHRoZXJlIGlzIGEgbmV3IHBvdGVudGlhbCBvdXRsaWVyIHRoYXQgd2UgZGlkIG5vdCBzZWUgZWFybGllci4gIFRoZXJlIGlzIGF0IGxlYXN0IG9uZSBvYnNlcnZhdGlvbiB3aGVyZSB0aGUgYFNhbGVfUHJpY2VgIGlzIG5lYXIgemVyby4gIEluIGZhY3QsIGZ1cnRoZXIgaW52ZXN0aWdhdGlvbiBpZGVudGlmaWVzIHR3byBvYnNlcnZhdGlvbnMsIG9uZSB3aXRoIGEgYFNhbGVfUHJpY2VgIG9mIFwkMTIsNzg5IGFuZCBhbm90aGVyIGF0IFwkMTMsMTAwLgoKYGBge3IgbG9ndHJhbnMsIGZpZy5oZWlnaHQ9MywgZmlnLndpZHRoPTV9CmdncGxvdChhbWVzLCBhZXMoU2FsZV9QcmljZSkpICsKICBnZW9tX2hpc3RvZ3JhbShiaW5zID0gMTAwKSArCiAgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gYygxNTAwMDAsIDE3MDAwMCksIGNvbG9yID0gInJlZCIsIGx0eSA9ICJkYXNoZWQiKSArCiAgc2NhbGVfeF9sb2cxMCgKICAgIGxhYmVscyA9IHNjYWxlczo6ZG9sbGFyLCAKICAgIGJyZWFrcyA9IGMoNTAwMDAsIDEyNTAwMCwgMzAwMDAwKQogICAgKQpgYGAKCkxldCdzIHRha2UgYSBjbG9zZXIgbG9vayBhdCB0aGUgc2Vjb25kIHR3byBpbnNpZ2h0cy4gIEZpcnN0LCB3ZSdsbCBjb25zaWRlciB0aGUgaXNzdWUgb2Ygbm9ybWFsaXR5LgoKSWYgeW91IHJlYWxseSB3YW50IHRvIGxvb2sgYXQgbm9ybWFsaXR5LCB0aGVuIFEtUSBwbG90cyBhcmUgYSBncmVhdCB2aXN1YWwgdG8gYXNzZXNzIChGaWcgeHgpLiBUaGlzIGdyYXBoIHBsb3RzIHRoZSBjdW11bGF0aXZlIHZhbHVlcyB3ZSBoYXZlIGluIG91ciBkYXRhIGFnYWluc3QgdGhlIGN1bXVsYXRpdmUgcHJvYmFiaWxpdHkgb2YgYSBwYXJ0aWN1bGFyIGRpc3RyaWJ1dGlvbiAodGhlIGRlZmF1bHQgaXMgYSBub3JtYWwgZGlzdHJpYnV0aW9uKS4gSW4gZXNzZW5jZSwgdGhpcyBwbG90IGNvbXBhcmVzIHRoZSBhY3R1YWwgdmFsdWUgYWdhaW5zdCB0aGUgZXhwZWN0ZWQgdmFsdWUgdGhhdCB0aGUgc2NvcmUgc2hvdWxkIGhhdmUgaW4gYSBub3JtYWwgZGlzdHJpYnV0aW9uLiBJZiB0aGUgZGF0YSBhcmUgbm9ybWFsbHkgZGlzdHJpYnV0ZWQgdGhlIHBsb3Qgd2lsbCBkaXNwbGF5IGEgc3RyYWlnaHQgKG9yIG5lYXJseSBzdHJhaWdodCkgbGluZS4gSWYgdGhlIGRhdGEgZGV2aWF0ZXMgZnJvbSBub3JtYWxpdHkgdGhlbiB0aGUgbGluZSB3aWxsIGRpc3BsYXkgc3Ryb25nIGN1cnZhdHVyZSBvciAic25ha2luZy4iICBUaGVzZSBwbG90cyBpbGx1c3RyYXRlIGhvdyBtdWNoIHRoZSB1bnRyYW5zZm9ybWVkIHZhcmlhYmxlIGRldmlhdGVzIGZyb20gbm9ybWFsaXR5IHdoZXJlYXMgdGhlIGxvZyB0cmFuc2Zvcm1lZCB2YWx1ZXMgYWxpZ24gbXVjaCBjbG9zZXIgdG8gYSBub3JtYWwgZGlzdHJpYnV0aW9uLgoKYGBge3IgcXFwbG90LCBmaWcuaGVpZ2h0PTMsIGZpZy53aWR0aD03fQpwYXIobWZyb3cgPSBjKDEsIDIpKQoKIyBub24tbG9nIHRyYW5zZm9ybWVkCnFxbm9ybShhbWVzJFNhbGVfUHJpY2UsIG1haW4gPSAiVW50cmFuc2Zvcm1lZFxuTm9ybWFsIFEtUSBQbG90IikKcXFsaW5lKGFtZXMkU2FsZV9QcmljZSkKCiMgbG9nIHRyYW5zZm9ybWVkCnFxbm9ybShsb2coYW1lcyRTYWxlX1ByaWNlKSwgbWFpbiA9ICJMb2cgVHJhbnNmb3JtZWRcbk5vcm1hbCBRLVEgUGxvdCIpCnFxbGluZShsb2coYW1lcyRTYWxlX1ByaWNlKSkKYGBgCgpJIGFsc28gbWVudGlvbmVkIGhvdyB3ZSBvYnRhaW5lZCBhIG5ldyBpbnNpZ2h0IHJlZ2FyZGluZyBhIG5ldyBwb3RlbnRpYWwgb3V0bGllciB0aGF0IHdlIGRpZCBub3Qgc2VlIGVhcmxpZXIuICBTbyBmYXIgb3VyIGhpc3RvZ3JhbSBpZGVudGlmaWVkIHBvdGVudGlhbCBvdXRsaWVycyBhdCB0aGUgbG93ZXIgZW5kIGFuZCB1cHBlciBlbmQgb2YgdGhlIHNhbGUgcHJpY2Ugc3BlY3RydW0uIFVuZm9ydHVuYXRlbHkgaGlzdG9ncmFtcyBhcmUgbm90IHZlcnkgZ29vZCBhdCBkZWxpbmVhdGluZyBvdXRsaWVycy4gIFJhdGhlciwgd2UgY2FuIHVzZSBhIGJveHBsb3Qgd2hpY2ggZG9lcyBhIGJldHRlciBqb2IgaWRlbnRpZnlpbmcgc3BlY2lmaWMgb3V0bGllcnMuCgpCb3hwbG90cyBhcmUgYW4gYWx0ZXJuYXRpdmUgd2F5IHRvIGlsbHVzdHJhdGUgdGhlIGRpc3RyaWJ1dGlvbiBvZiBhIHZhcmlhYmxlIGFuZCBpcyBhIGNvbmNpc2Ugd2F5IHRvIGlsbHVzdHJhdGUgdGhlIHN0YW5kYXJkIHF1YW50aWxlcyBhbmQgb3V0bGllcnMgb2YgZGF0YS4gQXMgRmlndXJlIFhYIGluZGljYXRlcywgdGhlIGJveCBpdHNlbGYgZXh0ZW5kcywgbGVmdCB0byByaWdodCwgZnJvbSB0aGUgMXN0IHF1YXJ0aWxlIHRvIHRoZSAzcmQgcXVhcnRpbGUuIFRoaXMgbWVhbnMgdGhhdCBpdCBjb250YWlucyB0aGUgbWlkZGxlIGhhbGYgb2YgdGhlIGRhdGEuIFRoZSBsaW5lIGluc2lkZSB0aGUgYm94IGlzIHBvc2l0aW9uZWQgYXQgdGhlIG1lZGlhbi4gVGhlIGxpbmVzICh3aGlza2VycykgY29taW5nIG91dCBlaXRoZXIgc2lkZSBvZiB0aGUgYm94IGV4dGVuZCB0byAxLjUgaW50ZXJxdWFydGlsZSByYW5nZXMgKElRUnMpIGZyb20gdGhlIHF1YXJ0aWxlcy4gVGhlc2UgZ2VuZXJhbGx5IGluY2x1ZGUgbW9zdCBvZiB0aGUgZGF0YSBvdXRzaWRlIHRoZSBib3guIE1vcmUgZGlzdGFudCB2YWx1ZXMsIGNhbGxlZCBvdXRsaWVycywgYXJlIGRlbm90ZWQgc2VwYXJhdGVseSBieSBpbmRpdmlkdWFsIHBvaW50cy4gTm93IHdlIGhhdmUgYSBtb3JlIGFuYWx5dGljYWxseSBzcGVjaWZpYyBhcHByb2FjaCB0byBpZGVudGlmeWluZyBvdXRsaWVycy4gCgo8Y2VudGVyPgo8aW1nIHNyYz0iaHR0cHM6Ly93d3cubGVhbnNpZ21hY29ycG9yYXRpb24uY29tL3dwL3dwLWNvbnRlbnQvdXBsb2Fkcy8yMDE1LzEyL0JveC1QbG90LU1UQl8wMS5wbmciIGFsdD0iR2VuZXJpYyBCb3ggUGxvdCIgd2lkdGg9IjUwMCIgdnNwYWNlPSIyMCI+CjwvY2VudGVyPgoKVGhlcmUgYXJlIHR3byBlZmZpY2llbnQgZ3JhcGhzIHRvIGdldCBhbiBpbmRpY2F0aW9uIG9mIHBvdGVudGlhbCBvdXRsaWVycyBpbiBvdXIgZGF0YS4gIFRoZSBjbGFzc2ljIGJveHBsb3Qgb24gdGhlIGxlZnQgd2lsbCBpZGVudGlmeSBwb2ludHMgYmV5b25kIHRoZSB3aGlza2VycyB3aGljaCBhcmUgYmV5b25kICQxLjUqSVFSJCBmcm9tIHRoZSBmaXJzdCBhbmQgdGhpcmQgcXVhbnRpbGUuICBUaGlzIGlsbHVzdHJhdGVzIHRoZXJlIGFyZSBzZXZlcmFsIGFkZGl0aW9uYWwgb2JzZXJ2YXRpb25zIHRoYXQgd2UgbWF5IG5lZWQgdG8gYXNzZXNzIGFzIG91dGxpZXJzIHRoYXQgd2VyZSBub3QgZXZpZGVudCBpbiBvdXIgaGlzdG9ncmFtLiAgSG93ZXZlciwgd2hlbiBsb29raW5nIGF0IGEgYm94cGxvdCB3ZSBsb3NlIGluc2lnaHQgaW50byB0aGUgc2hhcGUgb2YgdGhlIGRpc3RyaWJ1dGlvbi4gIEEgdmlvbGluIHBsb3Qgb24gdGhlIHJpZ2h0IHByb3ZpZGVzIHVzIGEgc2ltaWxhciBjaGFydCBhcyB0aGUgYm94cGxvdCBidXQgd2UgbG9zZSBpbnNpZ2h0IGludG8gdGhlIHF1YW50aWxlcyBvZiBvdXIgZGF0YSBhbmQgb3V0bGllcnMgYXJlIG5vdCBwbG90dGVkIChoZW5jZSB0aGUgcmVhc29uIEkgcGxvdCBgZ2VvbV9wb2ludGAgcHJpb3IgdG8gYGdlb21fdmlvbGluYCkuICBWaW9saW4gcGxvdHMgd2lsbCBjb21lIGluIGhhbmR5IGxhdGVyIHdoZW4gd2Ugc3RhcnQgdG8gdmlzdWFsaXplIG11bHRpcGxlIGRpc3RyaWJ1dGlvbnMgYWxvbmcgc2lkZSBlYWNoIG90aGVyLgoKYGBge3IgYm94cGxvdCwgZmlnLmFsaWduPSdjZW50ZXInfQpwMSA8LSBnZ3Bsb3QoYW1lcywgYWVzKCJ2YXIiLCBTYWxlX1ByaWNlKSkgKwogIGdlb21fYm94cGxvdChvdXRsaWVyLmFscGhhID0gLjI1KSArCiAgc2NhbGVfeV9sb2cxMCgKICAgIGxhYmVscyA9IHNjYWxlczo6ZG9sbGFyLCAKICAgIGJyZWFrcyA9IHF1YW50aWxlKGFtZXMkU2FsZV9QcmljZSkKICApCgpwMiA8LSBnZ3Bsb3QoYW1lcywgYWVzKCJ2YXIiLCBTYWxlX1ByaWNlKSkgKwogIGdlb21fcG9pbnQoKSArCiAgZ2VvbV92aW9saW4oKSArCiAgc2NhbGVfeV9sb2cxMCgKICAgIGxhYmVscyA9IHNjYWxlczo6ZG9sbGFyLCAKICAgIGJyZWFrcyA9IHF1YW50aWxlKGFtZXMkU2FsZV9QcmljZSkKICApCgpncmlkRXh0cmE6OmdyaWQuYXJyYW5nZShwMSwgcDIsIG5jb2wgPSAyKQpgYGAKClRoZSBib3hwbG90IHN0YXJ0cyB0byBhbnN3ZXIgdGhlIHF1ZXN0aW9uIG9mIHdoYXQgcG90ZW50aWFsIG91dGxpZXJzIGV4aXN0IGluIHlvdXIgZGF0YS4gT3V0bGllcnMgaW4gZGF0YSBjYW4gZGlzdG9ydCBwcmVkaWN0aW9ucyBhbmQgYWZmZWN0IHRoZWlyIGFjY3VyYWN5LiBDb25zZXF1ZW50bHksIGl0cyBpbXBvcnRhbnQgdG8gdW5kZXJzdGFuZCBpZiBvdXRsaWVycyBhcmUgcHJlc2VudCBhbmQsIGlmIHNvLCB3aGljaCBvYnNlcnZhdGlvbnMgYXJlIGNvbnNpZGVyZWQgb3V0bGllcnMuIEJveHBsb3RzIHByb3ZpZGUgYSB2aXN1YWwgYXNzZXNzbWVudCBvZiBwb3RlbnRpYWwgb3V0bGllcnMgd2hpbGUgdGhlIGBvdXRsaWVyc2AgcGFja2FnZSBwcm92aWRlcyBhIG51bWJlciBvZiB1c2VmdWwgZnVuY3Rpb25zIHRvIHN5c3RlbWF0aWNhbGx5IGV4dHJhY3QgdGhlc2Ugb3V0bGllcnMuIFRoZSBtb3N0IHVzZWZ1bCBmdW5jdGlvbiBpcyB0aGUgYHNjb3Jlc2AgZnVuY3Rpb24sIHdoaWNoIGNvbXB1dGVzIG5vcm1hbCwgdCwgY2hpLXNxdWFyZWQsIElRUiBhbmQgTUFEIHNjb3JlcyBvZiB0aGUgZ2l2ZW4gZGF0YSB3aGljaCB5b3UgY2FuIHVzZSB0byBmaW5kIG9ic2VydmF0aW9uKHMpIHRoYXQgbGllIGJleW9uZCBhIGdpdmVuIHZhbHVlLgoKSGVyZSwgSSB1c2UgdGhlIGBvdXRsaWVyczo6c2NvcmVgIGZ1bmN0aW9uIHRvIGV4dHJhY3QgdGhvc2Ugb2JzZXJ2YXRpb25zIGJleW9uZCB0aGUgd2hpc2tlcnMgaW4gb3VyIGJveHBsb3QgYW5kIHRoZW4gdXNlIGEgc3RlbSBhbmQgbGVhZiBwbG90IHRvIGFzc2VzcyB0aGVtLiAgQSBzdGVtIGFuZCBsZWFmIHBsb3QgaXMgYSBzcGVjaWFsIHRhYmxlIHdoZXJlIGVhY2ggZGF0YSB2YWx1ZSBpcyBzcGxpdCBpbnRvIGEgInN0ZW0iICh0aGUgZmlyc3QgZGlnaXQgb3IgZGlnaXRzKSBhbmQgYSAibGVhZiIgKHVzdWFsbHkgdGhlIHNlY29uZCBkaWdpdCkuICBTaW5jZSB0aGUgZGVjaW1hbCBwb2ludCBpcyBsb2NhdGVkIDUgZGlnaXRzIHRvIHRoZSByaWdodCBvZiB0aGUgInwiIiB0aGUgbGFzdCBzdGVtIG9mICI3IiBhbmQgYW5kIGZpcnN0IGxlYWYgb2YgIjUiIG1lYW5zIGFuIG91dGxpZXIgZXhpc3RzIGF0IGFyb3VuZCBcJDc1MCwwMDAuICBUaGUgbGFzdCBzdGVtIG9mICI3IiBhbmQgYW5kIHNlY29uZCBsZWFmIG9mICI2IiBtZWFucyBhbiBvdXRsaWVyIGV4aXN0cyBhdCBhcm91bmQgXCQ3NjAsMDAwLiAgVGhpcyBpcyBhIGNvbmNpc2Ugd2F5IHRvIHNlZSBhcHByb3hpbWF0ZWx5IHdoZXJlIG91ciBvdXRsaWVycyBhcmUuICBJbiBmYWN0LCBJIGNhbiBub3cgc2VlIHRoYXQgSSBoYXZlIDI4IGxvd2VyIGVuZCBvdXRsaWVycyByYW5naW5nIGZyb20gXCQxMCwwMDAtXCQ2MCwwMDAgYW5kIDMyIHVwcGVyIGVuZCBvdXRsaWVycyByYW5naW5nIGZyb20gXCQ0NTAsMDAwLVwkNzYwLDAwMC4KCmBgYHtyIHN0ZW19Cm91dGxpZXJzIDwtIG91dGxpZXJzOjpzY29yZXMobG9nKGFtZXMkU2FsZV9QcmljZSksIHR5cGUgPSAiaXFyIiwgbGltID0gMS41KQpzdGVtKGFtZXMkU2FsZV9QcmljZVtvdXRsaWVyc10pCmBgYAoKQW5vdGhlciB1c2VmdWwgcGxvdCBmb3IgdW5pdmFyaWF0ZSBhc3Nlc3NtZW50IGluY2x1ZGVzIHRoZSAqc21vb3RoZWQqIGhpc3RvZ3JhbSBpbiB3aGljaCBhIG5vbi1wYXJhbWV0cmljIGFwcHJvYWNoIGlzIHVzZWQgdG8gZXN0aW1hdGUgdGhlIGRlbnNpdHkgZnVuY3Rpb24uIERpc3BsYXlpbmcgaW4gZGVuc2l0eSBmb3JtIGp1c3QgbWVhbnMgdGhlIHktYXhpcyBpcyBub3cgaW4gYSBwcm9iYWJpbGl0eSBzY2FsZSB3aGVyZSB0aGUgcHJvcG9ydGlvbiBvZiB0aGUgZ2l2ZW4gdmFsdWUgKG9yIGJpbiBvZiB2YWx1ZXMpIHRvIHRoZSBvdmVyYWxsIHBvcHVsYXRpb24gaXMgZGlzcGxheWVkLiBJbiBlc3NlbmNlLCB0aGUgeS1heGlzIHRlbGxzIHlvdSB0aGUgZXN0aW1hdGVkIHByb2JhYmlsaXR5IG9mIHRoZSB4LWF4aXMgdmFsdWUgb2NjdXJyaW5nLiAgVGhpcyByZXN1bHRzIGluIGEgKnNtb290aGVkKiBjdXJ2ZSBrbm93biBhcyB0aGUgZGVuc2l0eSBwbG90IHRoYXQgYWxsb3dzIHVzIHZpc3VhbGl6ZSB0aGUgZGlzdHJpYnV0aW9uLiAgU2luY2UgdGhlIGZvY3VzIG9mIGEgZGVuc2l0eSBwbG90IGlzIHRvIHZpZXcgdGhlIG92ZXJhbGwgZGlzdHJpYnV0aW9uIHJhdGhlciB0aGFuIGluZGl2aWR1YWwgYmluIG9ic2VydmF0aW9ucyB3ZSBsb3NlIGluc2lnaHQgaW50byBob3cgbWFueSBvYnNlcnZhdGlvbnMgb2NjdXIgYXQgY2VydGFpbiB4IHZhbHVlcy4gIENvbnNlcXVlbnRseSwgaXQgY2FuIGJlIGhlbHBmdWwgdG8gdXNlIGBnZW9tX3J1Z2Agd2l0aCBgZ2VvbV9kZW5zaXR5YCB0byBoaWdobGlnaHQgd2hlcmUgY2x1c3RlcnMsIG91dGxpZXJzLCBhbmQgZ2FwcyBvZiBvYnNlcnZhdGlvbnMgYXJlIG9jY3VyaW5nLgoKYGBge3IgZGVuc2l0eSwgZmlnLmFsaWduPSdjZW50ZXInLCBmaWcud2lkdGg9OSwgZmlnLmhlaWdodD00fQpwMSA8LSBnZ3Bsb3QoYW1lcywgYWVzKFNhbGVfUHJpY2UpKSArCiAgZ2VvbV9kZW5zaXR5KCkKCnAyIDwtIGdncGxvdChhbWVzLCBhZXMoU2FsZV9QcmljZSkpICsKICBnZW9tX2RlbnNpdHkoKSArCiAgZ2VvbV9ydWcoKQoKZ3JpZEV4dHJhOjpncmlkLmFycmFuZ2UocDEsIHAyLCBucm93ID0gMSkKYGBgCgpPZnRlbiB5b3Ugd2lsbCBzZWUgZGVuc2l0eSBwbG90cyBsYXllcmVkIG9udG8gaGlzdG9ncmFtcy4gVG8gbGF5ZXIgdGhlIGRlbnNpdHkgcGxvdCBvbnRvIHRoZSBoaXN0b2dyYW0gd2UgbmVlZCB0byBmaXJzdCBkcmF3IHRoZSBoaXN0b2dyYW0gYnV0IHRlbGwgZ2dwbG90IHRvIGhhdmUgdGhlIHktYXhpcyBpbiBkZW5zaXR5IGZvcm0gcmF0aGVyIHRoYW4gY291bnQuIFlvdSBjYW4gdGhlbiBhZGQgdGhlIGBnZW9tX2RlbnNpdHlgIGZ1bmN0aW9uIHRvIGFkZCB0aGUgZGVuc2l0eSBwbG90IG9uIHRvcC4KCmBgYHtyIGRlbnNpdHkyLCBmaWcuYWxpZ249J2NlbnRlcicsIGZpZy53aWR0aD02LCBmaWcuaGVpZ2h0PTN9CmdncGxvdChhbWVzLCBhZXMoU2FsZV9QcmljZSkpICsKICBnZW9tX2hpc3RvZ3JhbShhZXMoeSA9IC4uZGVuc2l0eS4uKSwKICAgICAgICAgICAgICAgICBiaW53aWR0aCA9IDUwMDAsIGNvbG9yID0gImdyZXkzMCIsIGZpbGwgPSAid2hpdGUiKSArCiAgZ2VvbV9kZW5zaXR5KGFscGhhID0gLjIsIGZpbGwgPSAiYW50aXF1ZXdoaXRlMyIpCmBgYAoKWW91IG1heSBhbHNvIGJlIGludGVyZXN0ZWQgdG8gc2VlIGlmIHRoZXJlIGFyZSBhbnkgc3lzdGVtYXRpYyBncm91cGluZ3Mgd2l0aCBob3cgdGhlIGRhdGEgaXMgc3RydWN0dXJlZC4gIEZvciBleGFtcGxlLCB1c2luZyBiYXNlIFIncyBgcGxvdGAgZnVuY3Rpb24gd2l0aCBqdXN0IHRoZSBgU2FsZV9QcmljZWAgd2lsbCBwbG90IHRoZSBzYWxlIHByaWNlIHZlcnN1cyB0aGUgaW5kZXggKHJvdykgbnVtYmVyIG9mIGVhY2ggb2JzZXJ2YXRpb24uICBJbiB0aGUgcGxvdCBiZWxvdyB3ZSBzZWUgYSBwYXR0ZXJuIHdoaWNoIGluZGljYXRlcyB0aGF0IGdyb3VwaW5ncyBvZiBob21lcyB3aXRoIGhpZ2ggdmVyc3VzIGxvd2VyIHNhbGUgcHJpY2VzIGFyZSBjb25jZW50cmF0ZWQgdG9nZXRoZXIgdGhyb3VnaG91dCB0aGUgZGF0YSBzZXQuIAoKYGBge3IgaW5kZXhwbG90LCBmaWcuYWxpZ249J2NlbnRlcicsIGZpZy53aWR0aD03LCBmaWcuaGVpZ2h0PTN9CnBsb3QoYW1lcyRTYWxlX1ByaWNlKQpgYGAKCgoKVGhlcmUgYXJlIGFsc28gYSBjb3VwbGUgcGxvdHMgdGhhdCBjYW4gY29tZSBpbiBoYW5keSB3aGVuIGRlYWxpbmcgd2l0aCBzbWFsbGVyIGRhdGEgc2V0cy4gIEZvciBleGFtcGxlLCB0aGUgZG90cGxvdCBiZWxvdyBwcm92aWRlcyBtb3JlIGNsYXJpdHkgdGhhbiB0aGUgaGlzdG9ncmFtIGZvciB2aWV3aW5nIHRoZSBkaXN0cmlidXRpb24gb2YgYG1wZ2AgaW4gdGhlIGJ1aWx0LWluIGBtdGNhcnNgIGRhdGFzZXQgd2l0aCBvbmx5IDMyIG9ic2VydmF0aW9ucy4gIEFuIGFsdGVybmF0aXZlIHRvIHRoaXMgd291bGQgYmUgdXNpbmcgYSBzdHJpcCBjaGFydCAoc2VlIGBzdHJpcGNoYXJ0YCkuIAoKYGBge3IgZG90cGxvdCwgZmlnLmFsaWduPSdjZW50ZXInLCBmaWcud2lkdGg9OSwgZmlnLmhlaWdodD00fQpwMSA8LSBnZ3Bsb3QobXRjYXJzLCBhZXMoeCA9IG1wZykpICsKICBnZW9tX2RvdHBsb3QobWV0aG9kID0gImhpc3RvZG90IiwgYmlud2lkdGggPSAxKSArCiAgZ2d0aXRsZSgiZG90cGxvdCIpCgpwMiA8LSBnZ3Bsb3QobXRjYXJzLCBhZXMoeCA9IG1wZykpICsKICBnZW9tX2hpc3RvZ3JhbShiaW53aWR0aCA9IDEpICsKICBnZ3RpdGxlKCJoaXN0b2dyYW0iKQoKZ3JpZEV4dHJhOjpncmlkLmFycmFuZ2UocDEsIHAyLCBucm93ID0gMSkKYGBgCgpBcyBkZW1vbnN0cmF0ZWQsIHNldmVyYWwgcGxvdHMgZXhpc3QgZm9yIGV4YW1pbmluZyB1bml2YXJpYXRlIGNvbnRpbnVvdXMgdmFyaWFibGVzLiAgU2V2ZXJhbCBleGFtcGVzIHdlcmUgcHJvdmlkZWQgaGVyZSBidXQgc3RpbGwgbW9yZSBleGlzdCAoaS5lLiBmcmVxdWVuY3kgcG9seWdvbiwgYmVhbnBsb3QsIHNoaWZ0ZWQgaGlzdG9ncmFtcykuICBUaGVyZSBpcyBzb21lIGdlbmVyYWwgYWR2aWNlIHRvIGZvbGxvdyBzdWNoIGFzIGhpc3RvZ3JhbXMgYmVpbmcgcG9vciBmb3Igc21hbGwgZGF0YSBzZXRzLCBkb3RwbG90cyBiZWluZyBwb29yIGZvciBsYXJnZSBkYXRhIHNldHMsIGhpc3RvZ3JhbXMgYmVpbmcgcG9vciBmb3IgaWRlbnRpZnlpbmcgb3V0bGllciBjdXQtb2ZmcywgYm94cGxvdHMgYmVpbmcgZ29vZCBmb3Igb3V0bGllcnMgYnV0IG9ic2N1cmluZyBtdWx0aW1vZGFsaXR5LiAgQ29uc2VxZW50bHksIGl0IGlzIGltcG9ydGFudCB0byBkcmF3IGEgdmFyaWV0eSBvZiBwbG90cy4gIE1vcmVvdmVyLCBpdCBpcyBpbXBvcnRhbnQgdG8gYWRqdXN0IHBhcmFtZXRlcnMgd2l0aGluIHBsb3RzIChpLmUuIGJpbndpZHRoLCBheGlzIHRyYW5zZm9ybWF0aW9uIGZvciBza2V3ZWQgZGF0YSkgdG8gZ2V0IGEgY29tcHJlaGVuc2l2ZSBwaWN0dXJlIG9mIHRoZSB2YXJpYWJsZSBvZiBjb25jZXJuLgoKTmV4dCwgd2UnbGwgYXNzZXNzIGhvdyB3ZSBjYW4gZ2FpbiBpbnNpZ2h0IGludG8gZGlzdHJpYnV0aW9ucyBvZiBjYXRlZ29yaWNhbCB2YXJpYWJsZXMuCgojIFZpc3VhbGl6aW5nIENhdGVnb3JpY2FsIFZhcmlhYmxlcwoKQSBjYXRlZ29yaWNhbCB2YXJpYWJsZSBpcyBhIHZhcmlhYmxlIHRoYXQgY2FuIHRha2Ugb24gb25lIG9mIGEgbGltaXRlZCwgYW5kIHVzdWFsbHkgZml4ZWQsIG51bWJlciBvZiBwb3NzaWJsZSB2YWx1ZXMsIGFzc2lnbmluZyBlYWNoIGluZGl2aWR1YWwgb3Igb3RoZXIgdW5pdCBvZiBvYnNlcnZhdGlvbiB0byBhIHBhcnRpY3VsYXIgZ3JvdXAgb3Igbm9taW5hbCBjYXRlZ29yeSBvbiB0aGUgYmFzaXMgb2Ygc29tZSBxdWFsaXRhdGl2ZSBwcm9wZXJ0eSAoaS5lLiBnZW5kZXIsIGdyYWRlLCBtYW51ZmFjdHVyZXIpLiBUaGVyZSBhcmUgYSBmZXcgZGlmZmVyZW50IHBsb3RzIHRoYXQgY2FuIGVmZmVjdGl2ZWx5IGNvbW11bmljYXRlIGZlYXR1cmVzIG9mIGNhdGVnb3JpY2FsIHZhcmlhYmxlcy4gIEZlYXR1cmVzIHdlIGFyZSBnZW5lcmFsbHkgaW50ZXJlc3RlZCBpbiBpbmNsdWRlOgoKLSBDb3VudCBvZiBlYWNoIGNhdGVnb3J5Ci0gUHJvcG9ydGlvbiBvZiBlYWNoIGNhdGVnb3J5Ci0gSW1iYWxhbmNlZCBjYXRlZ29yaWVzCi0gTWlzbGFiZWxlZCBjYXRlZ29yaWVzCgpCYXIgY2hhcnRzIGFyZSBvbmUgb2YgdGhlIG1vc3QgY29tbW9ubHkgdXNlZCBkYXRhIHZpc3VhbGl6YXRpb25zIGZvciBjYXRlZ29yaWNhbCB2YXJpYWJsZXMuIEJhciBjaGFydHMgZGlzcGxheSB0aGUgbGV2ZWxzIG9mIGEgY2F0ZWdvcmljYWwgdmFyaWFibGUgb2YgaW50ZXJlc3QgKHR5cGljYWxseSkgYWxvbmcgdGhlIHgtYXhpcyBhbmQgdGhlIGxlbmd0aCBvZiB0aGUgYmFyIGlsbHVzdHJhdGVzIHRoZSB2YWx1ZSBhbG9uZyB0aGUgeS1heGlzLiBDb25zZXF1ZW50bHksIHRoZSBsZW5ndGggb2YgdGhlIGJhciBpcyB0aGUgcHJpbWFyeSB2aXN1YWwgY3VlIGluIGEgYmFyIGNoYXJ0IGFuZCBpbiBhIHVuaXZhcmlhdGUgdmlzdWFsaXphdGlvbiB0aGlzIGxlbmd0aCByZXByZXNlbnRzIGNvdW50cyBvZiBjYXNlcyBpbiB0aGF0IHBhcnRpY3VsYXIgbGV2ZWwuCgpJZiB3ZSBsb29rIGF0IHRoZSBnZW5lcmFsIHpvbmluZyBjbGFzc2lmaWNhdGlvbiBmb3IgZWFjaCBwcm9wZXJ0eSBzb2xkIGluIG91ciBgYW1lc2AgZGF0YXNldCB3ZSBzZWUgdGhhdCB0aGUgbWFqb3JpdHkgb2YgYWxsIHByb3BlcnRpZXMgZmFsbCB3aXRoaW4gb25lIGNhdGVnb3J5LiBIZXJlLCBgZ2VvbV9iYXJgIHNpbXBseSBjb3VudHMgdXAgYWxsIG9ic2VydmF0aW9ucyBmb3IgZWFjaCB6b25pbmcgbGV2ZWwuCgpgYGB7ciBiYXIxLCBmaWcud2lkdGg9NiwgZmlnLmhlaWdodD00fQpnZ3Bsb3QoYW1lcywgYWVzKE1TX1pvbmluZykpICsKICBnZW9tX2JhcigpCmBgYAoKSGVyZSwgYE1TX1pvbmluZ2AgcmVwcmVzZW50cyBhIG5vbWluYWwgY2F0ZWdvcmljYWwgdmFyaWFibGUgd2hlcmUgdGhlcmUgaXMgbm8gbG9naWNhbCBvcmRlcmluZyBvZiB0aGUgbGFiZWxzOyB0aGV5IHNpbXBseSByZXByZXNlbnQgbXV0dWFsbHkgZXhjbHVzaXZlIGxldmVscyB3aXRoaW4gb3VyIHZhcmlhYmxlLiAgVG8gZ2V0IGJldHRlciBjbGFyaXR5IG9mIG5vbWluYWwgdmFyaWFibGVzIHdlIGNhbiBtYWtlIHNvbWUgcmVmaW5lbWVudHMuICBIZXJlIEkgdXNlIGBkcGx5cjo6Y291bnRgIHRvIGNvdW50IHRoZSBvYnNlcnZhdGlvbnMgaW4gZWFjaCBsZXZlbCBwcmlvciB0byBwbG90dGluZy4gIEluIHRoZSBzZWNvbmQgcGxvdCBJIHVzZSBgbXV0YXRlYCB0byBjb21wdXRlIHRoZSBwZXJjZW50IHRoYXQgZWFjaCBsZXZlbCBtYWtlcyB1cCBvZiBhbGwgb2JzZXJ2YXRpb25zLiAgSSB0aGVuIGZlZWQgdGhlc2Ugc3VtbWFyaXplZCBkYXRhIGludG8gYGdncGxvdGAgd2hlcmUgSSBjYW4gYHJlb3JkZXJgIHRoZSBgTVNfWm9uaW5nYCB2YXJpYWJsZSBmcm9tIG1vc3QgZnJlcXVlbnQgdG8gbGVhc3QgYW5kIHRoZW4gYXBwbHkgYGNvb3JkX2ZsaXBgIHRvIHJvdGF0ZSB0aGUgcGxvdCBhbmQgbWFrZSBpdCBlYXNpZXIgdG8gcmVhZCB0aGUgbGV2ZWwgY2F0ZWdvcmllcy4gIEFsc28sIG5vdGljZSB0aGF0IG5vdyBJIGZlZWRpbmcgYW4geCAoYE1TX1pvbmluZ2ApIGFuZCB5IChgbmAgaW4gdGhlIGxlZnQgcGxvdCBhbmQgYHBjdGAgaW4gdGhlIHJpZ2h0IHBsb3QpIGFyZ3VtZW50cyBzbyBJIGFwcGx5IGBnZW9tX2NvbGAgcmF0aGVyIHRoYW4gYGdlb21fYmFyYC4KCmBgYHtyIGJhcjIsIGZpZy53aWR0aD05LCBmaWcuaGVpZ2h0PTN9CiMgdG90YWwgY291bnQKcDEgPC0gYW1lcyAlPiUgCiAgY291bnQoTVNfWm9uaW5nKSAlPiUKICBnZ3Bsb3QoYWVzKHJlb3JkZXIoTVNfWm9uaW5nLCBuKSwgbikpICsKICBnZW9tX2NvbCgpICsKICBjb29yZF9mbGlwKCkgKwogIGdndGl0bGUoIlRvdGFsIGNvdW50IikKCiMgcGVyY2VudCBvZiB3aG9sZQpwMiA8LSBhbWVzICU+JSAKICBjb3VudChNU19ab25pbmcpICU+JQogIG11dGF0ZShwY3QgPSBuIC8gc3VtKG4pKSAlPiUKICBnZ3Bsb3QoYWVzKHJlb3JkZXIoTVNfWm9uaW5nLCBwY3QpLCBwY3QpKSArCiAgZ2VvbV9jb2woKSArCiAgY29vcmRfZmxpcCgpICsKICBnZ3RpdGxlKCJQZXJjZW50IG9mIHdob2xlIikKCmdyaWRFeHRyYTo6Z3JpZC5hcnJhbmdlKHAxLCBwMiwgbnJvdyA9IDEpCmBgYAoKTm93IHdlIGNhbiBzZWUgdGhhdCBwcm9wZXJ0aWVzIHpvbmVkIGFzIHJlc2lkZW50aWFsIGxvdyBkZW5zaXR5IG1ha2UgdXAgbmVhcmx5IDgwJSBvZiBhbGwgb2JzZXJ2YXRpb25zIC4gV2UgYWxzbyBzZWUgdGhhdCBwcm9wZXJ0aWVzIHpvbmVkIGFzIGFnZ3JpY3VsdHVyYWwgKGBBX2FncmApLCBpbmR1c3RyaWFsIChgSV9hbGxgKSwgY29tbWVyY2lhbCAoYENfYWxsYCksIGFuZCByZXNpZGVudGlhbCBoaWdoIGRlbnNpdHkgbWFrZSB1cCBhIHZlcnkgc21hbGwgYW1vdW50IG9mIG9ic2VydmF0aW9ucy4gIEluIGZhY3QsIGJlbG93IHdlIHNlZSB0aGF0IHRoZXNlIGltYmFsYW5jZWQgY2F0ZWdvcnkgbGV2ZWxzIGVhY2ggbWFrZSB1cCBsZXNzIHRoYW4gMVwlIG9mIGFsbCBvYnNlcnZhdGlvbnMuICAKCmBgYHtyfQphbWVzICU+JSAKICBjb3VudChNU19ab25pbmcpICU+JQogIG11dGF0ZShwY3QgPSBuIC8gc3VtKG4pKSAlPiUKICBhcnJhbmdlKHBjdCkKYGBgCgpUaGlzIGltYmFsYW5jZWQgbmF0dXJlIGNhbiBjYXVzZSBwcm9ibGVtcyBpbiBmdXR1cmUgYW5hbHl0aWMgbW9kZWxzIHNvIGl0IG1heSBtYWtlIHNlbnNlIHRvIGNvbWJpbmUgdGhlc2UgaW5mcmVxdWVudCBsZXZlbHMgaW50byBhbiAib3RoZXIiIGNhdGVnb3J5LiBBbiBlYXN5IHdheSB0byBkbyB0aGF0IGlzIHRvIHVzZSBgZmN0X2x1bXBgLlteZmFjdG9yc10gIEhlcmUgd2UgdXNlIGBuID0gMmAgdG8gcmV0YWluIHRoZSB0b3AgMiBsZXZlbHMgaW4gb3VyIHZhcmlhYmxlIGFuZCBjb25kZW5zZSB0aGUgcmVtYWluaW5nIGludG8gYW4gIm90aGVyIiBjYXRlZ29yeS4gIFlvdSBjYW4gc2VlIHRoYXQgdGhpcyBjb21iaW5lZCBjYXRlZ29yeSBzdGlsbCByZXByZXNlbnRzIGxlc3MgdGhhbiAxMCUgb2YgYWxsIG9ic2VydmF0aW9ucy4KCmBgYHtyIGJhcjMsIGZpZy53aWR0aD02LCBmaWcuaGVpZ2h0PTN9CmFtZXMgJT4lIAogIG11dGF0ZShNU19ab25pbmcgPSBmY3RfbHVtcChNU19ab25pbmcsIG4gPSAyKSkgJT4lIAogIGNvdW50KE1TX1pvbmluZykgJT4lCiAgbXV0YXRlKHBjdCA9IG4gLyBzdW0obikpICU+JQogIGdncGxvdChhZXMocmVvcmRlcihNU19ab25pbmcsIHBjdCksIHBjdCkpICsKICBnZW9tX2NvbCgpICsKICBjb29yZF9mbGlwKCkKYGBgCgpCYXNpYyBiYXIgY2hhcnRzIHN1Y2ggYXMgdGhlc2UgYXJlIGdyZWF0IHdoZW4gdGhlIG51bWJlciBvZiBjYXRlZ29yeSBsZXZlbHMgaXMgc21hbGxlci4gIEhvd2V2ZXIsIGFzIHRoZSBudW1iZXIgb2YgbGV2ZWxzIGluY3JlYXNlIHRoZSB0aGljayBuYXR1cmUgb2YgdGhlIGJhciBjYW4gYmUgZGlzdHJhY3RpbmcuICBDbGV2ZWxhbmQgZG90IHBsb3RzIGFuZCBsb2xsaXBvcCBjaGFydHMgYXJlIHVzZWZ1bCBmb3IgYXNzZXNzaW5nIHRoZSBmcmVxdWVuY3kgb3IgcHJvcG9ydGlvbiBvZiBtYW55IGxldmVscyB3aGlsZSBtaW5pemluZyB0aGUgYW1vdW50IG9mIGluayBvbiB0aGUgZ3JhcGhpYy4KCkZvciBleGFtcGxlLCBpZiB3ZSBhc3Nlc3MgdGhlIGZyZXF1ZW5jaWVzIGFuZCBwcm9wb3J0aW9ucyBvZiBob21lIHNhbGVzIGJ5ICB0aGUgMzggZGlmZmVyZW50IG5laWdoYm9yaG9vZHMgYSBkb3RwbG90IHNpbXBsaWZpZXMgdGhlIGNoYXJ0LgoKYGBge3IgZG90LCBmaWcuaGVpZ2h0PTYsIGZpZy53aWR0aD03fQphbWVzICU+JSAgCiAgY291bnQoTmVpZ2hib3Job29kKSAlPiUKICBtdXRhdGUocGN0ID0gbiAvIHN1bShuKSkgJT4lCiAgZ2dwbG90KGFlcyhwY3QsIHJlb3JkZXIoTmVpZ2hib3Job29kLCBwY3QpKSkgKwogIGdlb21fcG9pbnQoKQpgYGAKClNpbWlsYXIgdG8gdGhlIENsZXZlbGFuZCBkb3QgcGxvdCwgYSBsb2xsaXBvcCBjaGFydCBtaW5pbWl6ZXMgdGhlIHZpc3VhbCBpbmsgYnV0IHVzZXMgYSBsaW5lIHRvIGRyYXcgdGhlIHJlYWRlcnMgYXR0ZW50aW9uIHRvIHRoZSBzcGVjaWZpYyB4LWF4aXMgdmFsdWUgYWNoaWV2ZWQgYnkgZWFjaCBjYXRlZ29yeS4gSW4gdGhlIGxvbGxpcG9wIGNoYXJ0IHdlIHVzZSBgZ2VvbV9zZWdtZW50YCB0byBwbG90IHRoZSBsaW5lcyBhbmQgd2UgZXhwbGljaXRseSBzdGF0ZSB0aGF0IHdlIHdhbnQgdGhlIGxpbmVzIHRvIHN0YXJ0IGF0IGB4ID0gMGAgYW5kIGV4dGVuZCB0byB0aGUgbmVpZ2hib3Job29kIHZhbHVlIHdpdGggYHhlbmQgPSBwY3RgLiBXZSBzaW1wbHkgbmVlZCB0byBpbmNsdWRlIGB5ID0gbmVpZ2hib3Job29kYCBhbmQgYHllbmQgPSBuZWlnaGJvcmhvb2RgIHRvIHRlbGwgUiB0aGUgbGluZXMgYXJlIGhvcml6b250YWxseSBhdHRhY2hlZCB0byBlYWNoIG5laWdoYm9yaG9vZC4KCmBgYHtyIGRvdDEsIGZpZy5oZWlnaHQ9NiwgZmlnLndpZHRoPTd9CmFtZXMgJT4lICAKICBjb3VudChOZWlnaGJvcmhvb2QpICU+JQogIG11dGF0ZShwY3QgPSBuIC8gc3VtKG4pKSAlPiUKICBnZ3Bsb3QoYWVzKHBjdCwgcmVvcmRlcihOZWlnaGJvcmhvb2QsIHBjdCkpKSArCiAgZ2VvbV9wb2ludCgpICsKICBnZW9tX3NlZ21lbnQoYWVzKHggPSAwLCB4ZW5kID0gcGN0LCB5ID0gTmVpZ2hib3Job29kLCB5ZW5kID0gTmVpZ2hib3Job29kKSwgc2l6ZSA9IC4xNSkKYGBgCgoKU29tZXRpbWVzIHdlIGhhdmUgY2F0ZWdvcmljYWwgZGF0YSB0aGF0IGhhdmUgbmF0dXJhbCwgb3JkZXJlZCBjYXRlZ29yaWVzLiAgVGhlc2UgdHlwZXMgb2YgY2F0ZWdvcmljYWwgdmFyaWFibGVzIGNhbiBiZSBvcmRpbmFsIG9yIGludGVydmFsLiAgQW4gb3JkaW5hbCB2YXJpYWJsZSBpcyBvbmUgaW4gd2hpY2ggdGhlIG9yZGVyIG9mIHRoZSB2YWx1ZXMgY2FuIGJlIGltcG9ydGFudCBidXQgdGhlIGRpZmZlcmVuY2VzIGJldHdlZW4gZWFjaCBvbmUgaXMgbm90IHJlYWxseSBrbm93bi4gIEZvciBleGFtcGxlLCBvdXIgYGFtZXNgIGRhdGEgY2F0ZWdvcml6ZXMgdGhlIHF1YWxpdHkgb2Yga2l0Y2hlbnMgaW50byBmaXZlIGJ1Y2tldHMgYW5kIHRoZXNlIGJ1Y2tldHMgaGF2ZSBhIG5hdHVyYWwgb3JkZXIgdGhhdCBpcyBub3QgY2FwdHVyZWQgd2l0aCBhIHJlZ3VsYXIgYmFyIGNoYXJ0LgoKYGBge3Igb3JkMSwgZmlnLmhlaWdodD0zLCBmaWcud2lkdGg9NX0KZ2dwbG90KGFtZXMsIGFlcyhLaXRjaGVuX1F1YWwpKSArIAogIGdlb21fYmFyKCkKYGBgCgpIZXJlLCByYXRoZXIgdGhhbiBvcmRlciBieSBmcmVxdWVuY3kgaXQgbWF5IGJlIGltcG9ydGFudCB0byBvcmRlciB0aGUgYmFycyBieSB0aGUgbmF0dXJhbCBvcmRlciBvZiB0aGUgcXVhbGl0eSBsYWJsZXM6ICBQb29yLCBGYWlyLCBUeXBpY2FsLCBHb29kLCBFeGNlbGxlbnQuICBUaGlzIGNhbiBwcm92aWRlIGJldHRlciBpbnNpZ2h0IGludG8gd2hlcmUgbW9zdCBvYnNlcnZhdGlvbnMgZmFsbCB3aXRoaW4gdGhpcyBzcGVjdHJ1bSBvZiBxdWFsaXR5LiAgVG8gZG8gdGhpcyB3ZSByZW9yZGVyIHRoZSBmYWN0b3IgbGV2ZWxzIHdpdGggYGZjdF9yZWxldmVsYCBhbmQgbm93IGl0cyBlYXNpZXIgdG8gc2VlIHRoYXQgbW9zdCBob21lcyBoYXZlIGF2ZXJhZ2UgdG8gc2xpZ2h0bHkgYWJvdmUgYXZlcmFnZSBxdWFsaXR5IGtpdGNoZW5zLgoKYGBge3Igb3JkMiwgZmlnLmhlaWdodD0zLCBmaWcud2lkdGg9NX0KYW1lcyAlPiUKICBtdXRhdGUoS2l0Y2hlbl9RdWFsID0gZmN0X3JlbGV2ZWwoS2l0Y2hlbl9RdWFsLCAiUG9vciIsICJGYWlyIiwgIlR5cGljYWwiLCAiR29vZCIpKSAlPiUKICBnZ3Bsb3QoYWVzKEtpdGNoZW5fUXVhbCkpICsgCiAgZ2VvbV9iYXIoKQpgYGAKCldlIG1heSBhbHNvIGhhdmUgYSBjYXRlZ29yaWNhbCB2YXJpYWJsZSB0aGF0IGhhcyBzZXQgaW50ZXJ2YWxzIGFuZCBtYXkgZXZlbiBiZSBpZGVudGlmaWVkIGJ5IGludGVnZXIgdmFsdWVzLiAgRm9yIGV4YW1wbGUsIG91ciBkYXRhIGlkZW50aWZpZXMgdGhlIG1vbnRoIGVhY2ggaG9tZSB3YXMgc29sZCBidXQgdXNlcyBpbnRlZ2VyIHZhbHVlcyB0byByZXByZXNlbnQgdGhlIG1vbnRocy4gIEluIHRoaXMgY2FzZSB3ZSBkbyBub3QgbmVlZCB0byByZW9yZGVyIG91ciBmYWN0b3IgbGV2ZWxzIGJ1dCB3ZSBzaG91bGQgZW5zdXJlIHdlIHZpc3VhbGl6ZSB0aGVzZSBhcyBkaXNjcmV0ZSBmYWN0b3IgbGV2ZWxzIChub3RlIGhvdyBJIGFwcGx5IGBmYWN0b3IoTW9fU29sZClgIHdpdGhpbiBnZ3Bsb3QpIHNvIHRoYXQgdGhlIGhvbWUgc2FsZSBjb3VudHMgYXJlIGFwcHJvcHJpYXRlbHkgYnVja2V0ZWQgaW50byBlYWNoIG1vbnRoLiAgCgpgYGB7ciBpbnQxLCBmaWcud2lkdGg9NywgZmlnLmhlaWdodD01fQpwMSA8LSBnZ3Bsb3QoYW1lcywgYWVzKE1vX1NvbGQpKSArIAogIGdlb21fYmFyKCkKCnAyIDwtIGdncGxvdChhbWVzLCBhZXMoZmFjdG9yKE1vX1NvbGQpKSkgKyAKICBnZW9tX2JhcigpCgpncmlkRXh0cmE6OmdyaWQuYXJyYW5nZShwMSwgcDIsIG5yb3cgPSAyKQpgYGAKCgpCYXIgY2hhcnRzIGNhbiBhbHNvIGlsbHVzdHJhdGUgaG93IG91ciBtaXNzaW5nIHZhbHVlcyBhcmUgZGlzYnVyc2VkIGFjcm9zcyBjYXRlZ29yaWNhbCB2YXJpYWJsZXMuICBVc2luZyB0aGUgYE1BU1M6OnN1cnZleWAgZGF0YSAoc2luY2Ugb3VyIGBhbWVzYCBkYXRhIGRvZXMgbm90IGhhdmUgYW55IG1pc3NpbmcgZGF0YSkgd2UgY2FuIG1ha2Ugc21hbGwgbXVsdGlwbGVzIChtb3JlIG9uIHRoaXMgaW4gdGhlIG5leHQgc2VjdGlvbikgdXNpbmcgYGZhY2V0X3dyYXBgIHRvIHZpc3VhbGl6ZSB0aGUgYE5BYHMuIAoKYGBge3IgbWlzc2luZzEsIGZpZy5oZWlnaHQ9NCwgZmlnLndpZHRoPTh9Ck1BU1M6OnN1cnZleSAlPiUKICBzZWxlY3QoU2V4LCBFeGVyLCBTbW9rZSwgRm9sZCwgQ2xhcCwgTS5JKSAlPiUKICBnYXRoZXIodmFyLCB2YWx1ZSwgU2V4Ok0uSSkgJT4lCiAgZ2dwbG90KGFlcyh2YWx1ZSkpICsKICBnZW9tX2JhcigpICsKICBmYWNldF93cmFwKH4gdmFyLCBzY2FsZXMgPSAiZnJlZSIpCmBgYAoKT3IgaW4gc29tZSBjYXNlcyBvYnNlcnZhdGlvbnMgYXJlIG5vdCBsYWJlbGVkIGNvcnJlY3RseS4gIElmIHdlIGxvb2sgYXQgdGhlIGBFbWJhcmtlZGAgdmFyaWFibGUgaW4gdGhlIGB0aXRhbmljYCBwYWNrYWdlIHdlIHNlZSB0aGF0IHRoZSBsZXZlbHMgYXJlIGxhYmVsZWQgYXMgQywgUSwgYW5kIFM7IGhvd2V2ZXIsIHRoZXJlIGFyZSB0d28gY2FzZXMgdGhhdCBoYXZlIG5vIGxhYmVsICh0aGVzZSB2YWx1ZXMgYXJlIGNvZGVkIGFzIGAiImAgaW4gdGhlIGFjdHVhbCBkYXRhIHNldCkuICBUaGVzZSBhcmUgbWlzc2luZyB2YWx1ZXMgdGhhdCBhcmUganVzdCBub3QgY29kZWQgYXMgYE5BYHMuICBGb3IgbW9kZWxpbmcgcHVycG9zZXMgd2Ugd291bGQgbGlrZWx5IHJlY29kZSB0aGVzZSBhcyBlaXRoZXIgYE5BYHMgb3IgaW1wdXRlIHRoZW0gYXMgb25lIG9mIHRoZSBvdGhlciB0aHJlZSBsZXZlbHMgKEMsIFEsIG9yIFMpLgoKYGBge3IgbWlzbGFiZWxlZCwgZmlnLmhlaWdodD0zLCBmaWcud2lkdGg9NH0KZ2dwbG90KHRpdGFuaWM6OnRpdGFuaWNfdHJhaW4sIGFlcyhFbWJhcmtlZCkpICsKICBnZW9tX2JhcigpCmBgYAoKQmFyIGNoYXJ0cyBhbmQgdGhlaXIgY291c2lucyBhcmUgYSBzaW1wbGUgZm9ybSBvZiB2aXN1YWwgZGlzcGxheSwgeWV0IHRoZXkgY2FuIHByb3ZpZGUgbXVjaCBpbmZvcm1hdGlvbiBhYm91dCBvdXIgY2F0ZWdvcmljYWwgdmFyaWFibGVzLiAgV2hldGhlciB2aWV3aW5nIG5vbWluYWwsIG9yZGluYWwsIG9yIGludGVydmFsIGRhdGEgd2UgY2FuIG1ha2UgbWlub3IgYWRqdXN0bWVudHMgaW4gb3VyIGJhciBjaGFydHMgdG8gaGlnaGxpZ2h0IHRoZSBpbXBvcnRhbnQgZmVhdHVyZXMgb2Ygb3VyIHZhcmlhYmxlcy4KCiMgVmlzdWFsaXppbmcgUmVsYXRpb25zaGlwcyBhbmQgQXNzb2NpYXRpb25zIGZvciBhIENvbnRpbnVvdXMgUmVzcG9uc2UKCkhhdmluZyBhIHNvbGlkIHVuZGVyc3RhbmRpbmcgb2YgdW5pdmFyaWF0ZSBkaXN0cmlidXRpb25zIGlzIGltcG9ydGFudDsgaG93ZXZlciwgbW9zdCBhbmFseXNlcyB3YW50IHRvIHRha2UgdGhlIG5leHQgc3RlcCB1bmRlcnN0YW5kIGFzc29jaWF0aW9ucyBhbmQgcmVsYXRpb25zaGlwcyBhY3Jvc3MgZGlmZmVyZW50IHZhcmlhYmxlcy4gRmVhdHVyZXMgd2UgYXJlIGdlbmVyYWxseSBpbnRlcmVzdGVkIGluIGluY2x1ZGU6CgotIEFzc29jaWF0aW9ucwotIE91dGxpZXJzCi0gQ2x1c3RlcnMKLSBHYXBzCi0gQmFycmllcnMKLSBDaGFuZ2UgcG9pbnRzCgoKT25lIG9mIHRoZSBtb3N0IHBvcHVsYXIgcGxvdHMgdG8gYXNzZXNzIGFzc29jaWF0aW9uIGlzIHRoZSBzY2F0dGVyIHBsb3QuICBUaGUgc2NhdHRlciBwbG90IGhlbHBzIHVzIHRvIHNlZSBtdWx0aXBsZSBmZWF0dXJlcyBiZXR3ZWVuIHR3byBjb250aW51b3VzIHZhcmlhYmxlcy4gIEhlcmUgd2UgbG9vayB0aGUgcmVsYXRpb25zaGlwIGJldHdlZW4gYFNhbGVfUHJpY2VgIGFuZCB0b3RhbCBhYm92ZSBncm91bmQgc3F1YXJlIGZvb3RhZ2UgKGBHcl9MaXZfQXJlYWApLiBBIGZldyBmZWF0dXJlcyB0aGF0IHBvcCBvdXQgZnJvbSB0aGlzIHBsb3QgaW5jbHVkZXM6CgotIEFzc29jaWF0aW9uczogVGhlcmUgaXMgYSBwb3NpdGl2ZSByZWxhdGlvbnNoaXAgYmV0d2VlbiB0aGVzZSB0d28gdmFyaWFibGVzLiAgQXMgdG90YWwgYWJvdmUgZ3JvdW5kIHNxdWFyZSBmb290YWdlIGluY3JlYXNlcyB0aGUgc2FsZSBwcmljZSBhbHNvIGluY3JlYXNlcy4KLSBPdXRsaWVyczogU2V2ZXJhbCBvdXRsaWVycyBhcHBlYXIgaW4gbXVsdGlwbGUgZGlyZWN0aW9ucy4gIFR3byBvdXRsaWVycyBhcHBlYXIgYXQgdGhlIHRvcCBvZiB0aGUgY2hhcnQgc3VnZ2VzdGluZyB0aGVzZSBhcmUgbGFyZ2VyIHRoYW4gbm9ybWFsIGhvbWVzIHRoYXQgc29sZCBmb3IgdmVyeSBoaWdoIHByaWNlcy4gIFdlIGFsc28gc2VlIHRocmVlIG91dGxpZXJzIGF0IHRoZSBmYXIgcmlnaHQgb2YgdGhlIGNoYXJ0IHN1Z2dlc3RpbmcgdGhlc2UgaG9tZXMgaGF2ZSB2ZXJ5IGxhcmdlIHNxdWFyZSBmb290YWdlIGJ1dCBzb2xkIGZvciBhdmVyYWdlIHNhbGUgcHJpY2VzLgotIENsdXN0ZXJzOiBHaXZlIHRoZSBsYXJnZSBudW1iZXIgb2YgcG9pbnRzIHRoZXJlIGlzIGEgbG90IG9mIG92ZXJwbG90dGluZywgd2hpY2ggaXMgd2h5IEkgaW5jb3Jwb3JhdGVkIGBhbHBoYSA9IC4zYCB0byBpbmNyZWFzZSB0cmFuc3BhcmVuY3kuICBUaGlzIGFsbG93cyB1cyB0byBzZWUgdGhlIGNsdXN0ZXJpbmcgb2YgZGF0YSBwb2ludHMgaW4gdGhlIGNlbnRlciBvZiB0aGUgdmFyaWFibGUgcmVsYXRpb25zaGlwLgotIEJhcnJpZXJzOiBUaGUgb3V0ZXIgbGltaXRzIG9mIG91ciBwb2ludCBjbHVzdGVyaW5nIHNob3dzIHVzIHRoYXQgdGhlcmUgYXJlIGxpbWl0YXRpb25zIG9uIHRoZSBzYWxlIHByaWNlIGZvciBnaXZlbiByYW5nZXMgb2Ygc3F1YXJlIGZvb3RhZ2UuICBGb3IgZXhhbXBsZSwgaG9tZXMgd2l0aCBsZXNzIHRoYW4gMSwwMDAgc3F1YXJlIGZlZXQgYWJvdmUgZ3JvdW5kIGFwcGVhciB0byBoYXZlIGEgcHJpY2UgY2VpbGluZyBvZiBcJDIwMCwwMDAgb3IgbGVzcy4KCmBgYHtyfQpnZ3Bsb3QoYW1lcywgYWVzKHggPSBHcl9MaXZfQXJlYSwgeSA9IFNhbGVfUHJpY2UpKSArCiAgZ2VvbV9wb2ludChhbHBoYSA9IC4zKQpgYGAKClRoaXMgcmVsYXRpb25zaGlwIGFwcGVhcnMgdG8gYmUgZmFpcmx5IGxpbmVhciBidXQgaXQgaXMgdW5jbGVhci4gV2UgY2FuIGFkZCB0cmVuZCBsaW5lcyB0byBhc3Nlc3MgdGhlIGxpbmVhcml0eS4gIEluIHRoZSBiZWxvdyBwbG90IHdlIGFkZCBhIGxpbmVhciBsaW5lIHdpdGggYGdlb21fc21vb3RoKG1ldGhvZCA9ICJsbSIpYCBhbmQgdGhlbiB3ZSBhZGQgYSBub24tbGluZWFyIGxpbmUgKHRoZSBzZWNvbmQgYGdlb21fc21vb3RoYCB3aXRoIGEgc3BlY2lmaWVkIGBtZXRob2RgIGFkZHMgdXNlcyBhIGdlbmVyYWxpemVkIGFkZGl0aXZlIG1vZGVsKS4gIFRoaXMgYWxsb3dzIHVzIHRvIGFzc2VzcyBob3cgbm9uLWxpbmVhciBhIHJlbGF0aW9uc2hpcCBtYXkgYmUuICBPdXIgbmV3IHBsb3Qgc2hvd3MgdGhhdCBmb3IgaG9tZXMgd2l0aCBsZXNzIHRoYW4gMiwyNTAgc3F1YXJlIGZlZXQgdGhlIHJlbGF0aW9uc2hpcCBpcyBmYWlybHkgbGluZWFyOyBob3dldmVyLCBiZXlvbmQgMiwyNTAgc3F1YXJlIGZlZXQgd2Ugc2VlIHN0cm9uZyBkZXZpYXRpb25zIGZyb20gbGluZWFyaXR5LgoKQWxzbywgbm90ZSB0aGUgZnVubmVsaW5nIGluIHRoZSBsZWZ0IHNjYXR0ZXIgcGxvdC4gIFRoaXMgaXMgY2FsbGVkIGhldGVyb3NrZWRhc3RpY2l0eSAobm9uLWNvbnN0YW50IHZhcmlhbmNlKSBhbmQgdGhpcyBjYW4gY2F1c2UgY29uY2VybnMgd2l0aCBjZXJ0YWluIGZ1dHVyZSBtb2RlbGluZyBhcHByb2FjaGVzIChpLmUuIGZvcm1zIG9mIGxpbmVhciByZWdyZXNzaW9uKS4gIFdlIGNhbiBhc3Nlc3MgaWYgdHJhbnNmb3JtaW5nIG91ciB2YXJpYWJsZXMgY2FuIGFsbGV2aWF0ZSB0aGlzIGNvbmNlcm4gYnkgYWRkaW5nIGBzY2FsZV8/X2xvZzEwYC4gIFRoZSByaWdodCBwbG90IHNob3dzIHRoYXQgdHJhbnNmb3JtaW5nIG91ciB2YXJpYWJsZXMgbWFrZXMgb3VyIHZhcmlhYmlsaXR5IGFjcm9zcyB0aGUgcGxvdCBtb3JlIGNvbnN0YW50LiAgV2Ugc2VlIHRoYXQgZm9yIHRoZSBtYWpvcml0eSBvZiB0aGUgcGxvdCB0aGUgcmVsYXRpb25zaGlwIGlzIG5vdyBsaW5lYXIgd2l0aCB0aGUgZXhjZXB0aW9uIG9mIHRoZSB0d28gZW5kcyB3aGVyZSB3ZSBzZWUgdGhlIG5vbi1saW5lYXIgbGluZSBiZWluZyBwdWxsZWQgZG93bi4gIFRoaXMgc3VnZ2VzdHMgdGhhdCB0aGVyZSBhcmUgc29tZSBpbmZsdWVudGlhbCBvYnNlcnZhdGlvbnMgd2l0aCBsb3cgYW5kIGhpZ2ggc3F1YXJlIGZvb3RhZ2UgdGhhdCBhcmUgcHVsbGluZyB0aGUgZXhwZWN0ZWQgc2FsZSBwcmljZSBkb3duLiAgCgoKYGBge3IgZGJsX3NjYXR0ZXIsIGZpZy53aWR0aD04LCBmaWcuaGVpZ2h0PTR9CnAxIDwtIGdncGxvdChhbWVzLCBhZXMoeCA9IEdyX0xpdl9BcmVhLCB5ID0gU2FsZV9QcmljZSkpICsKICBnZW9tX3BvaW50KGFscGhhID0gLjMpICsKICBnZW9tX3Ntb290aChtZXRob2QgPSAibG0iLCBzZSA9IEZBTFNFLCBjb2xvciA9ICJyZWQiLCBsdHkgPSAiZGFzaGVkIikgKwogIGdlb21fc21vb3RoKHNlID0gRkFMU0UsIGx0eSA9ICJkYXNoZWQiKSArCiAgZ2d0aXRsZSgiTm9uLXRyYW5zZm9ybWVkIHZhcmlhYmxlcyIpCgpwMiA8LSBnZ3Bsb3QoYW1lcywgYWVzKHggPSBHcl9MaXZfQXJlYSwgeSA9IFNhbGVfUHJpY2UpKSArCiAgZ2VvbV9wb2ludChhbHBoYSA9IC4zKSArCiAgZ2VvbV9zbW9vdGgobWV0aG9kID0gImxtIiwgc2UgPSBGQUxTRSwgY29sb3IgPSAicmVkIiwgbHR5ID0gImRhc2hlZCIpICsKICBnZW9tX3Ntb290aChzZSA9IEZBTFNFLCBsdHkgPSAiZGFzaGVkIikgKwogIHNjYWxlX3hfbG9nMTAoKSArCiAgc2NhbGVfeV9sb2cxMCgpICsKICBnZ3RpdGxlKCJsb2ctdHJhbnNmb3JtZWQgdmFyaWFibGVzIikKCmdyaWRFeHRyYTo6Z3JpZC5hcnJhbmdlKHAxLCBwMiwgbnJvdyA9IDEpCmBgYAoKU2NhdHRlciBwbG90cyBjYW4gYWxzbyBzaWduYWwgZGlzdGluY3QgY2x1c3RlcmluZyBvciBnYXBzLiAgRm9yIGV4YW1wbGUsIGlmIHdlIHBsb3QgYFNhbGVfUHJpY2VgIHZlcnN1cyBgR2FyYWdlX0FyZWFgIChsZWZ0KSB3ZSBzZWUgYSBjb3VwbGUgYXJlYXMgb2YgY29uY2VudHJhdGVkIHBvaW50cy4gIEJ5IGluY29ycG9yYXRpbmcgYSBkZW5zaXR5IHBsb3QgKG1pZGRsZSkgd2UgY2FuIGRyYXcgYXR0ZW50aW9uIHRvIHRoZSBjZW50ZXJzIG9mIHRoZXNlIGNsdXN0ZXJzIHdoaWNoIGFwcGVhcnMgdG8gYmUgbG9jYXRlZCBhdCBob21lcyB3aXRoIHplcm8gZ2FyYWdlIHNxdWFyZSBmb290YWdlIGFuZCBob21lcyB3aXRoIGp1c3Qgb3ZlciAyNTAgc3F1YXJlIGZlZXQgYW5kIGp1c3QgdW5kZXIgNTAwIHNxdWFyZSBmZWV0IG9mIGdhcmFnZSBhcmVhLiBXZSBjYW4gYWxzbyBjaGFuZ2Ugb3VyIHBsb3QgdG8gYSBoZXhiaW4gcGxvdCwgdGhhdCByZXBsYWNlcyBidW5jaGVzIG9mIHBvaW50cyB3aXRoIGEgbGFyZ2VyIGhleGFnb25hbCBzeW1ib2wuIFRoaXMgcHJvdmlkZXMgdXMgd2l0aCBhIGhlYXRtYXAtbGlrZSBwbG90IHRvIHNpZ25hbCBoaWdobHkgY29uY2VudHJhdGVkIHJlZ2lvbnMuICBJdCBhbHNvIGRvZXMgYSBiZXR0ZXIgam9iIGlkZW50aWZ5aW5nIGdhcHMgaW4gb3VyIGRhdGEgd2hlcmUgbm8gb2JzZXJ2YXRpb25zIGV4aXN0LgoKYGBge3IgZGVuc2l0eV9wbG90LCBmaWcud2lkdGg9OCwgZmlnLmhlaWdodD0zfQpwMSA8LSBnZ3Bsb3QoYW1lcywgYWVzKHggPSBHYXJhZ2VfQXJlYSwgeSA9IFNhbGVfUHJpY2UpKSArIAogIGdlb21fcG9pbnQoYWxwaGEgPSAuMikKCnAyIDwtIGdncGxvdChhbWVzLCBhZXMoeCA9IEdhcmFnZV9BcmVhLCB5ID0gU2FsZV9QcmljZSkpICsgCiAgZ2VvbV9wb2ludChhbHBoYSA9IC4yKSArIAogIGdlb21fZGVuc2l0eTJkKCkKCnAzIDwtIGdncGxvdChhbWVzLCBhZXMoeCA9IEdhcmFnZV9BcmVhLCB5ID0gU2FsZV9QcmljZSkpICsgCiAgZ2VvbV9oZXgoYmlucyA9IDUwLCBzaG93LmxlZ2VuZCA9IEZBTFNFKQoKZ3JpZEV4dHJhOjpncmlkLmFycmFuZ2UocDEsIHAyLCBwMywgbnJvdyA9IDEpCmBgYAoKV2hlbiBhc3Nlc3NpbmcgYSBjb250aW51b3VzIHZhcmlhYmxlIGFnYWluc3QgYSBjYXRlZ29yaWNhbCB2YXJpYWJsZSBhIHN0aXAgcGxvdCB3aWxsIGZvcm0uICBIZXJlIHdlIGFzc2VzcyB0aGUgYFNhbGVfUHJpY2VgIHRvIHRoZSBudW1iZXIgb2YgYWJvdmUgZ3JvdW5kIGJlZHJvb21zIChgQmVkcm9vbV9BYnZHcmApLiAgRHVlIHRvIHRoZSBzaXplIG9mIHRoaXMgZGF0YSBzZXQsIHRoZSB0b3AgbGVmdCBzdHJpcCBwbG90IGhhcyBhIGxvdCBvZiBvdmVybGFpZCBkYXRhIHBvaW50cy4gIFdlIGNhbiB1c2UgYGdlb21faml0dGVyYCB0byBhZGQgYSBsaXR0bGUgdmFyaWF0aW9uIHRvIG91ciBwbG90ICh0b3AgcmlnaHQpLCB3aGljaCBhbGxvd3MgdXMgdG8gc2VlIHdoZXJlIGhlYXZpZXIgY29uY2VudHJhdGlvbnMgb2YgcG9pbnRzIGV4aXN0LiBBbHRlcm5hdGl2ZWx5LCB3ZSBjYW4gdXNlIGJveHBsb3RzIGFuZCB2aW9saW4gcGxvdHMgdG8gY29tcGFyZSB0aGUgZGlzdHJpYnV0aW9ucyBvZiBgU2FsZV9QcmljZWAgdG8gYEJlZHJvb21fQWJ2R3JgLiAgRWFjaCBwbG90IHByb3ZpZGVzIGRpZmZlcmVudCBpbnNpZ2h0cyB0byB0aGUgZGlmZmVyZW50IGZlYXR1cmVzIChpLmUuIG91dGxpZXJzLCBjbHVzdGVyaW5nLCBtZWRpYW4gdmFsdWVzKS4KCgpgYGB7ciBpbnRfc2NhdHRlciwgZmlnLndpZHRoPTcsIGZpZy5oZWlnaHQ9NX0KcDEgPC0gZ2dwbG90KGFtZXMsIGFlcyh4ID0gZmFjdG9yKEJlZHJvb21fQWJ2R3IpLCB5ID0gU2FsZV9QcmljZSkpICsKICBnZW9tX3BvaW50KGFscGhhID0gLjMpCgpwMiA8LSBnZ3Bsb3QoYW1lcywgYWVzKHggPSBmYWN0b3IoQmVkcm9vbV9BYnZHciksIHkgPSBTYWxlX1ByaWNlKSkgKwogIGdlb21faml0dGVyKGFscGhhID0gLjUsIHdpZHRoID0gLjIpCgpwMyA8LSBnZ3Bsb3QoYW1lcywgYWVzKHggPSBmYWN0b3IoQmVkcm9vbV9BYnZHciksIHkgPSBTYWxlX1ByaWNlKSkgKwogIGdlb21fYm94cGxvdCgpCgpwNCA8LSBnZ3Bsb3QoYW1lcywgYWVzKHggPSBmYWN0b3IoQmVkcm9vbV9BYnZHciksIHkgPSBTYWxlX1ByaWNlKSkgKwogIGdlb21fdmlvbGluKCkKCmdyaWRFeHRyYTo6Z3JpZC5hcnJhbmdlKHAxLCBwMiwgcDMsIHA0LCBucm93ID0gMikKYGBgCgpBbiBhbHRlcm5hdGl2ZSBhcHByb2FjaCB0byB2aWV3IHRoZSBkaXN0cmlidXRpb24gb2YgYSBjb250aW51b3VzIHZhcmlhYmxlIGFjcm9zcyBtdWx0aXBsZSBjYXRlZ29yaWVzIGluY2x1ZGVzIG92ZXJsYXlpbmcgZGlzdHJpYnV0aW9uIHBsb3RzLiAgRm9yIGV4YW1wbGUsIHdlIGNvdWxkIGFzc2VzcyB0aGUgYFNhbGVfUHJpY2VgIG9mIGhvbWVzIGFjcm9zcyB0aGUgb3ZlcmFsbCBxdWFsaXR5IG9mIGhvbWVzLiAgV2UgY2FuIGRvIHRoaXMgd2l0aCBhIGZyZXF1ZW5jeSBwb2x5Z29uIChsZWZ0KSwgd2hpY2ggZGlzcGxheSB0aGUgb3V0bGluZSBvZiBhIGhpc3RvZ3JhbS4gSG93ZXZlciwgc2luY2Ugc29tZSBxdWFsaXR5IGxldmVscyBoYXZlIHZlcnkgbG93IGNvdW50cyBpdCBpcyB0b3VnaCB0byBzZWUgdGhlIGRpc3RyaWJ1dGlvbiBvZiBjb3N0cyB3aXRoaW4gZWFjaCBjYXRlZ29yeS4gIEEgYmV0dGVyIGFwcHJvYWNoIGlzIHRvIG92ZXJsYXkgZGVuc2l0eSBwbG90cyB3aGljaCBhbGxvd3MgdXMgdG8gc2VlIGhvdyBlYWNoIHF1YWxpdHkgbGV2ZWwncyBkaXN0cmlidXRpb24gZGlmZmVycyBmcm9tIG9uZSBhbm90aGVyLiAKCmBgYHtyLCBmaWcud2lkdGg9OCwgZmlnLmhlaWdodD01fQpwMSA8LSBnZ3Bsb3QoYW1lcywgYWVzKHggPSBTYWxlX1ByaWNlLCBjb2xvciA9IE92ZXJhbGxfUXVhbCkpICsKICBnZW9tX2ZyZXFwb2x5KCkgKwogIHNjYWxlX3hfbG9nMTAoYnJlYWtzID0gYyg1MCwgMTUwLCA0MDAsIDc1MCkgKiAxMDAwLCBsYWJlbHMgPSBzY2FsZXM6OmRvbGxhcikKICAKcDIgPC0gZ2dwbG90KGFtZXMsIGFlcyh4ID0gU2FsZV9QcmljZSwgY29sb3IgPSBPdmVyYWxsX1F1YWwsIGZpbGwgPSBPdmVyYWxsX1F1YWwpKSArCiAgZ2VvbV9kZW5zaXR5KGFscGhhID0gLjE1KSArCiAgc2NhbGVfeF9sb2cxMChicmVha3MgPSBjKDUwLCAxNTAsIDQwMCwgNzUwKSAqIDEwMDAsIGxhYmVscyA9IHNjYWxlczo6ZG9sbGFyKQoKZ3JpZEV4dHJhOjpncmlkLmFycmFuZ2UocDEsIHAyLCBucm93ID0gMikKYGBgCgpXaGVuIHRoZXJlIGFyZSBtYW55IGxldmVscyBpbiBhIGNhdGVnb3JpY2FsIHZhcmlhYmxlLCBvdmVybGFpZCBwbG90cyBiZWNvbWUgZGlmZmljdWx0IHRvIGRlY2lwaGVyLiAgUmF0aGVyIHRoYW4gb3ZlcmxheSBwbG90cywgd2UgY2FuIGFsc28gdXNlIHNtYWxsIG11bHRpcGxlcyB0byBjb21wYXJlIHRoZSBkaXN0cmlidXRpb24gb2YgYSBjb250aW51b3VzIHZhcmlhYmxlLiBKb3lwbG90cyBwcm92aWRlIGEgZm9ybSBvZiBzbWFsbCBtdWx0aXBsZXMgYnkgcGFydGlhbGx5IG92ZXJsYXBwaW5nIGRpc3RyaWJ1dGlvbiBwbG90cy4gVGhleSBjYW4gYmUgcXVpdGUgdXNlZnVsIGZvciB2aXN1YWxpemluZyBjaGFuZ2VzIGluIGNvbnRpbnVvdXMgZGlzdHJpYnV0aW9ucyBvdmVyIGRpc2NyZXRlIHZhcmlhYmxlIGxldmVscy4gIEluIHRoaXMgZXhhbXBsZSBJIHVzZSB0aGUgYGdnam95YCBwYWNrYWdlIHdoaWNoIHByb3ZpZGVzIGFuIGFkZC1vbiBgZ2VvbV9qb3lgIGZvciBnZ3Bsb3QuICBOb3cgd2UgZ2V0IGEgbXVjaCBjbGVhcmVyIHBpY3R1cmUgaG93IHRoZSBzYWxlcyBwcmljZSBkaWZmZXJzIGZvciBlYWNoIHF1YWxpdHkgbGV2ZWwuCgpgYGB7ciBqb3lwbG90LCBmaWcud2lkdGg9NywgZmlnLmhlaWdodD00fQpnZ3Bsb3QoYW1lcywgYWVzKHggPSBTYWxlX1ByaWNlLCB5ID0gT3ZlcmFsbF9RdWFsKSkgKyAKICBnZ2pveTo6Z2VvbV9qb3koKSArCiAgc2NhbGVfeF9jb250aW51b3VzKGxhYmVscyA9IHNjYWxlczo6ZG9sbGFyKQpgYGAKCkluIG1vc3QgYW5hbHlzZXMsIGRhdGEgYXJlIHVzdWFsbHkgbXVsdGl2YXJpYXRlIGJ5IG5hdHVyZSwgYW5kIHRoZSBhbmFseXRpY3MgYXJlIGRlc2lnbmVkIHRvIGNhcHR1cmUgYW5kIG1lYXN1cmUgbXVsdGl2YXJpYXRlIHJlbGF0aW9uc2hpcHMuICBWaXN1YWwgZXhwbG9yYXRpb24gc2hvdWxkIHRoZXJlIGZvcmUgYWxzbyBpbmNvcnBvcmF0ZSB0aGlzIGltcG9ydGFudCBhc3BlY3QuICBXZSBjYW4gZXh0ZW5kIHRoZXNlIGJhc2ljIHByaW5jaXBsZXMgYW5kIGFkZCBpbiBhZGRpdGlvbmFsIGZlYXR1cmVzIHRvIGFzc2VzcyBtdWx0aWRpbWVuc2lvbmFsIHJlbGF0aW9uc2hpcHMuICBPbmUgYXBwcm9hY2ggaXMgdG8gYWRkIGFkZGl0aW9uYWwgdmFyaWFibGVzIHdpdGggZmVhdHVyZXMgc3VjaCBhcyBjb2xvciwgc2hhcGUsIG9yIHNpemUuICBGb3IgZXhhbXBsZSwgaGVyZSB3ZSBjb21wYXJlIHRoZSBzYWxlcyBwcmljZSB0byBhYm92ZSBncm91bmQgc3F1YXJlIGZvb3RhZ2Ugb2YgaG9tZXMgd2l0aCBhbmQgd2l0aG91dCBjZW50cmFsIGFpciBjb25kaXRpb25pbmcuICBXZSBjYW4gc2VlIHRoYXQgdGhlcmUgYXJlIGZhciBtb3JlIGhvbWVzIHdpdGggY2VudHJhbCBhaXIgYW5kIHRoYXQgdGhvc2UgaG9tZXMgd2l0aG91dCBjZW50cmFsIGFpciB0ZW5kIHRvIGhhdmUgbGVzcyBzcXVhcmUgZm9vdGFnZSBhbmQgc2VsbCBmb3IgbG93ZXIgc2FsZXMgcHJpY2VzLgoKYGBge3IgbXVsdGlkaW0xLCBmaWcud2lkdGg9NiwgZmlnLmhlaWdodD01fQpnZ3Bsb3QoYW1lcywgYWVzKHggPSBHcl9MaXZfQXJlYSwgeSA9IFNhbGVfUHJpY2UsIGNvbG9yID0gQ2VudHJhbF9BaXIsIHNoYXBlID0gQ2VudHJhbF9BaXIpKSArCiAgZ2VvbV9wb2ludChhbHBoYSA9IC4zKSArCiAgc2NhbGVfeF9sb2cxMCgpICsKICBzY2FsZV95X2xvZzEwKCkKYGBgCgpIb3dldmVyLCBhcyBiZWZvcmUsIHdoZW4gdGhlcmUgYXJlIG1hbnkgbGV2ZWxzIGluIGEgY2F0ZWdvcmljYWwgdmFyaWFibGUgaXQgYmVjb21lcyBoYXJkIHRvIGNvbXBhcmUgZGlmZmVyZW5jZXMgYnkgb25seSBpbmNvcnBvcmF0aW5nIGNvbG9yIG9yIHNoYXBlIGZlYXR1cmVzLiAgQW4gYWx0ZXJuYXRpdmUgaXMgdG8gY3JlYXRlIHNtYWxsIG11bHRpcGxlcy4gSGVyZSB3ZSBjb21wYXJlIHRoZSByZWxhdGlvbnNoaXAgYmV0d2VlbiBzYWxlcyBwcmljZSBhbmQgYWJvdmUgZ3JvdW5kIHNxdWFyZSBmb290YWdlOyBob3dldmVyLCB3ZSBhc3Nlc3MgaG93IHRoaXMgcmVsYXRpb25zaGlwIG1heSBkaWZmZXIgYWNyb3NzIHRoZSBkaWZmZXJlbnQgaG91c2Ugc3R5bGVzIChpLmUuIG9uZSBzdG9yeSwgdHdvIHN0b3J5LCBldGMuKS4KCmBgYHtyIHNtYWxsbXVsdGksIGZpZy53aWR0aD04LCBmaWcuaGVpZ2h0PTV9CmdncGxvdChhbWVzLCBhZXMoeCA9IEdyX0xpdl9BcmVhLCB5ID0gU2FsZV9QcmljZSkpICsKICBnZW9tX3BvaW50KGFscGhhID0gLjMpICsKICBzY2FsZV94X2xvZzEwKCkgKwogIHNjYWxlX3lfbG9nMTAobGFiZWxzID0gc2NhbGVzOjpkb2xsYXIpICsKICBmYWNldF93cmFwKH4gSG91c2VfU3R5bGUsIG5yb3cgPSAyKSArCiAgdGhlbWVfYncoKQoKYGBgCgpXZSBjYW4gc3RhcnQgdG8gYWRkIHNldmVyYWwgb2YgdGhlIGZlYXR1cmVzIGRpc2N1c3NlZCBpbiB0aGlzIHNlY3Rpb24gdG8gaGlnaGxpZ2h0IG11dGxpdmFyaWF0ZSBmZWF0dXJlcy4gIEZvciBleGFtcGxlLCBoZXJlIHdlIGFzc2VzcyB0aGUgcmVsYXRpb25zaGlwIGJldHdlZW4gc2FsZXMgcHJpY2UgYW5kIGFib3ZlIGdyb3VuZCBzcXVhcmUgZm9vdGFibGUgZm9yIGhvbWVzIHdpdGggYW5kIHdpdGhvdXQgY2VudHJhbCBhaXIgY29uZGl0aW9uaW5nIGFuZCBhY3Jvc3MgdGhlIGRpZmZlcmVudCBob3VzaW5nIHN0eWxlcy4gIEZvciBlYWNoIGhvdXNlIHN0eWxlIGFuZCBjZW50cmFsIGFpciBjYXRlZ29yeSB3ZSBjYW4gc2VlIHdoZXJlIHRoZSB2YWx1ZXMgYXJlIGNsdXN0ZXJlZCBhbmQgaG93IHRoZSBsaW5lYXIgcmVsYXRpb25zaGlwIGNoYW5nZXMuICBGb3IgYWxsIGhvbWUgc3R5bGVzLCBob3VzZXMgd2l0aCBjZW50cmFsIGFpciBoYXZlIGEgaGlnaGVyIHNlbGxpbmcgcHJpY2Ugd2l0aCBhIHN0ZWVwZXIgc2xvcGUgdGhhbiB0aG9zZSB3aXRob3V0IGNlbnRyYWwgYWlyLiAgQWxzbywgdGhvc2UgcGxvdHMgd2l0aG91dCBkZW5zaXR5IG1hcmtpbmdzIGFuZCBsaW5lYXIgbGluZXMgZm9yIHRoZSBubyBjZW50cmFsIGFpciBjYXRlZ29yeSAocmVkKSB0ZWxsIHVzIHRoYXQgdGhlcmUgYXJlIG5vIG1vcmUgdGhhbiBvbmUgb2JzZXJ2YXRpb24gaW4gdGhlc2UgZ3JvdXBzOyBzbyB0aGlzIGlkZW50aWZpZXMgZ2FwcyBhY3Jvc3MgbXVsdGl2YXJpYXRlIGNhdGVnb3JpZXMgb2YgaW50ZXJlc3QuCgpgYGB7ciBtdWx0aXZhcjEsIGZpZy53aWR0aD04LCBmaWcuaGVpZ2h0PTV9CmdncGxvdChhbWVzLCBhZXMoeCA9IEdyX0xpdl9BcmVhLCB5ID0gU2FsZV9QcmljZSwgY29sb3IgPSBDZW50cmFsX0Fpciwgc2hhcGUgPSBDZW50cmFsX0FpcikpICsKICBnZW9tX3BvaW50KGFscGhhID0gLjMpICsKICBnZW9tX2RlbnNpdHkyZChhbHBoYSA9IC41KSArCiAgZ2VvbV9zbW9vdGgobWV0aG9kID0gImxtIiwgc2UgPSBGQUxTRSkgKwogIHNjYWxlX3hfbG9nMTAoKSArCiAgc2NhbGVfeV9sb2cxMChsYWJlbHMgPSBzY2FsZXM6OmRvbGxhcikgKwogIGZhY2V0X3dyYXAofiBIb3VzZV9TdHlsZSwgbnJvdyA9IDIpICsKICBnZ3RpdGxlKCJTYWxlIFByaWNlIHZzLiBBYm92ZSBHcm91bmQgU3EuRnQiLAogICAgICAgICAgc3VidGl0bGUgPSAiSG93IGRvZXMgY2VudHJhbCBhaXIgYW5kIGhvdXNlIHN0eWxlIGluZmx1ZW5jZSB0aGlzIHJlbGF0aW9uc2hpcD8iKSArCiAgdGhlbWVfYncoKQoKYGBgCgoKUGFyYWxsZWwgY29vcmRpbmF0ZSBwbG90cyAoUENQKSBhcmUgYWxzbyBhIGdyZWF0IHdheSB0byB2aXN1YWxpemUgY29udGludW91cyB2YXJpYWJsZXMgYWNyb3NzIG11bHRpcGxlIHZhcmlhYmxlcy4gSW4gdGhlc2UgcGxvdHMsIGEgdmVydGljYWwgYXhpcyBpcyBkcmF3biBmb3IgZWFjaCB2YXJpYWJsZS4gIFRoZW4gZWFjaCBvYnNlcnZhdGlvbiBpcyByZXByZXNlbnRlZCBieSBkcmF3aW5nIGEgbGluZSB0aGF0IGNvbm5lY3RzIGl0cyB2YWx1ZXMgb24gdGhlIGRpZmZlcmVudCBheGVzLCB0aGVyZWJ5IGNyZWF0aW5nIGEgbXVsdGl2YXJpYXRlIHByb2ZpbGUuICBUbyBjcmVhdGUgYSBQQ1AsIHdlIGNhbiB1c2UgYGdncGFyY29vcmRgIGZyb20gdGhlIGBHR2FsbHlgIHBhY2thZ2UuICBCeSBkZWZhdWx0LCBgZ2dwYXJjb29yZGAgd2lsbCBzdGFuZGFyZGl6ZSB0aGUgdmFyaWFibGVzIGJhc2VkIG9uIGEgWi1zY29yZSBkaXN0cmlidXRpb247IGhvd2V2ZXIsIGluIHRoaXMgZXhhbXBsZSBJIGNlbnRlciBzY2FsZSB0aGUgdmFyaWFibGVzLiAgT25lIGJlbmVmaXQgb2Ygb2YgYSBQQ1AgaXMgdGhhdCB5b3UgY2FuIHZpc3VhbGl6ZSB5b3VyIG9ic2VydmF0aW9ucyBhY3Jvc3MgY29udGludW91cyBhbmQgY2F0ZWdvcmljYWwgdmFyaWFibGVzLiAgSW4gdGhpcyBleGFtcGxlIEkgaW5jbHVkZSBgT3ZlcmFsbF9RdWFsYCB3aGljaCBpcyBhbiBvcmRlcmVkIGZhY3RvciB3aXRoIGxldmVscyAiVmVyeSBQb29yIiwgIlBvb3IiLCAiRmFpciIsIC4uLiwgIkV4Y2VsbGVudCIsICJWZXJ5IEV4Y2VsbGVudCIgaGF2aW5nIHZhbHVlcyBvZiAxLTEwLiAgCgpgYGB7ciBwYXJhbGxlbDEsIGZpZy53aWR0aD04LCBmaWcuaGVpZ2h0PTV9CnZhcmlhYmxlcyA8LSBjKCJTYWxlX1ByaWNlIiwgIlllYXJfQnVpbHQiLCAiWWVhcl9SZW1vZF9BZGQiLCAiT3ZlcmFsbF9RdWFsIikKCmFtZXMgJT4lCiAgc2VsZWN0KHZhcmlhYmxlcykgJT4lCiAgZ2dwYXJjb29yZChhbHBoYSA9IC4wNSwgc2NhbGUgPSAiY2VudGVyIikKYGBgCgoKCiMgVmlzdWFsaXppbmcgUmVsYXRpb25zaGlwcyBhbmQgQXNzb2NpYXRpb25zIGZvciBhIENhdGVnb3JpY2FsIFJlc3BvbnNlCgotIGZhY2V0dGVkIGJhciBjaGFydHMKLSBtb3NhaWMgcGxvdAotIGhlYXRtYXAKCiMgVmlzdWFsaXppbmcgRGF0YSBRdWFsaXR5CgotIE1pc3NpbmduZXNzCi0gT3V0bGllcnMKCgpbXmJhc2VSaGlzdF06IFdlIGNvdWxkIGFsc28gdXNlIGBoaXN0KGFtZXMkU2FsZV9QcmljZSlgIHRvIHByb2R1Y2UgYSBoaXN0b2dyYW0gd2l0aCBiYXNlIFIgZ3JhcGhpY3MuClteYmluc106IFRoZXNlIGFyZSBhcHByb3hpbWF0ZXMgYmVjYXVzZSB0aGUgYmlubmluZyB3aWxsIHJvdW5kIHRvIHdob2xlIG51bWJlcnMgKDEyLDgwMCByYXRoZXIgdGhhbiAxMiwzNzAuKQpbXnRyYW5zZl06IFR3byB0aGluZ3MgdG8gbm90ZSBoZXJlLiAxLiBJZiB5b3Ugd2FudCB0byBjaGFuZ2UgdGhlIGJpbndpZHRoIHlvdSBlaXRoZXIgbmVlZCB0byBmZWVkIGEgbG9nIHRyYW5mb3JtZWQgbnVtYmVyIHRvIGJpbndpZHRoIG9yLCBhcyBJIGRpZCwgaW5jcmVhc2UgdGhlIGJpbnMgYnkgdXNpbmcgYGJpbnMgPSAxMDBgLiAyLiBJZiB5b3UgaGF2ZSB2YWx1ZXMgb2YgemVybyBpbiB5b3VyIHZhcmlhYmxlIHRyeSBgc2NhbGVfeF9jb250aW51b3VzKHRyYW5zID0gImxvZzFwIiksIHdoaWNoIGFkZHMgMSBwcmlvciB0byB0aGUgbG9nIHRyYW5zZm9ybWF0aW9uLgpbXmZhY3RvcnNdOiBBIGdyZWF0IHJlc291cmNlIHRvIGxlYXJuIG1vcmUgYWJvdXQgd2hpY2ggeW91IGNhbiBsZWFybiBtb3JlIGFib3V0IG1hbmFnaW5nIGZhY3RvcnMgaXMgUiBmb3IgRGF0YSBTY2llbmNlLCBDaC4gMTUuCg==